diff options
Diffstat (limited to 'drivers')
30 files changed, 18836 insertions, 308 deletions
diff --git a/drivers/ata/sata_dwc_pmp.c b/drivers/ata/sata_dwc_pmp.c index df3b490f86e..54b69d67e35 100755 --- a/drivers/ata/sata_dwc_pmp.c +++ b/drivers/ata/sata_dwc_pmp.c @@ -1110,7 +1110,7 @@ static int dma_dwc_xfer_setup(struct ata_queued_cmd *qc, #if defined(CONFIG_APM82181) if (dma_ch == 0) { /* Buffer mode enabled, FIFO_MODE=0 */ - out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.high), 0x000000d); + out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.high), 0x0000009); /* Channel 0 bit[7:5] */ out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.low), 0x00000020); } else if (dma_ch == 1) { diff --git a/drivers/leds/led-class-3g.c b/drivers/leds/led-class-3g.c index c0c1c11cbdb..7b8e0fa45ed 100644 --- a/drivers/leds/led-class-3g.c +++ b/drivers/leds/led-class-3g.c @@ -20,59 +20,296 @@ #include <linux/err.h> #include <linux/ctype.h> #include <linux/leds.h> +#include <linux/errno.h> #include "leds.h" +#define MAX_USERS 32 +#define N_COLORS 4 +#define N_EVENTS 3 +#define USR_LEN 81 +#define EVENT_LEN 81 +#define INDEX_LEN 8 + + +/* LED users */ +#define EV_NAS_SYSTEM 0 /* Overall system: NAS ready, booting, shutdown... */ +#define EV_DISK_SMART 1 /* Disk SMART including temp., error lba, ...*/ +#define EV_DISK_IO 2 /* Disk read/write error */ +#define EV_RAID_CFG 3 /* RAID setup failure: assembling, formatting, rebuild ...*/ +#define EV_FW_UPDATE 4 /* NAS firmware update */ +#define EV_NETWORK 5 /* Network connectivity error */ +#define EV_VM 6 /* Volume manager */ + +char Led_user_arr[MAX_USERS][USR_LEN] = { "EV_NAS_SYSTEM", \ + "EV_DISK_SMART", \ + "EV_DISK_IO" , \ + "EV_RAID_CFG" , \ + "EV_FW_UPDATE" , \ + "EV_NETWORK" , \ + "EV_VM", \ + }; +/* LED event types */ +#define LED_STAT_OK 0 /* Happy user, normal operation */ +#define LED_STAT_ERR 1 /* User error, needs led indication */ +#define LED_STAT_IN_PROG 2 /* User doing something important, needs led indication */ + +char *Led_ev_arr[] = { "LED_STAT_OK", "LED_STAT_ERR", "LED_STAT_IN_PROG" }; + +char Color_map[MAX_USERS][N_EVENTS] = { {'g','r','w'}, /* EV_NAS_SYSTEM */ \ + {'g','y','w'}, /* EV_DISK_SMART */ \ + {'g','r','w'}, /* EV_DISK_IO */ \ + {'g','r','w'}, /* EV_RAID_CFG */ \ + {'g','r','w'}, /* EV_FW_UPDATE */ \ + {'g','y','w'}, /* EV_NETWORK */ \ + {'g','r','w'}, /* EV_VM */ \ + }; + +char Blink_map[MAX_USERS][N_EVENTS] = { {'n','n','n'}, /* EV_NAS_SYSTEM */ \ + {'n','y','n'}, /* EV_DISK_SMART */ \ + {'n','n','n'}, /* EV_DISK_IO */ \ + {'n','n','n'}, /* EV_RAID_CFG */ \ + {'n','n','n'}, /* EV_FW_UPDATE */ \ + {'n','y','n'}, /* EV_NETWORK */ \ + {'n','n','n'}, /* EV_VM */ \ + }; + +u32 Led_error_bits = 0; +int N_USERS = 7; /* default number of users */ + static struct class *leds_class; static void led_update_color(struct led_classdev *led_cdev) { - if (led_cdev->color_get) - led_cdev->color = led_cdev->color_get(led_cdev); + if (led_cdev->color_get) + led_cdev->color = led_cdev->color_get(led_cdev); } static ssize_t led_color_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) + struct device_attribute *attr, const char *buf, size_t size) { - struct led_classdev *led_cdev = dev_get_drvdata(dev); - unsigned long state = 9; - - switch (buf[0]) { - case 'r': /* red */ - state = 1; - break; - case 'g': /* green */ - state = 2; - break; - case 'b': /* blue */ - state = 3; - break; - case 'y': /* yellow */ - state = 4; - break; - case 'w': /* white */ - state = 5; - break; - case 'o': /* off */ - state = 0; - break; - default: - break; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + unsigned long state = 9; + char user[USR_LEN], event[EVENT_LEN], index_str[INDEX_LEN], color; + int i = 0, j = 0, found = 0, tmp = 0, edit_policy = 0; + int reg_user = -1, reg_event = -1, reg_color = -1; + const char * cptr = NULL; + long int index = -1; + char blink; + int reg_blink = 'n'; + + cptr = &buf[0]; + + /* check for 'register' event */ + // NB: Format of register event is + // register:event,status,color + if( cptr[8] == ':' ) { + if( !memcmp("register", cptr, 8) ) { + edit_policy = 1; + cptr = &buf[9]; + } + } + + /* parse user name */ + for( i = 0; i < (USR_LEN -1) && cptr[i]; i++ ) { + if( cptr[i] == ',' ) { + break; + } + user[i] = cptr[i]; + } + + /* null terminate user buf */ + user[i] = '\0'; + i++; /* skips the ',' delimiter */ + + + for( j = 0; (j < EVENT_LEN -1) && cptr[i] ; j++,i++ ) { + if( (cptr[i] == ',') || (cptr[i] == '\0') || (cptr[i] == '\n') ) { + if( cptr[i] == ',' ) { + cptr = &cptr[i+1]; + } + break; + } + event[j] = cptr[i]; + } + /* null terminate event buf */ + event[j] = '\0'; + + /* if editing policy, parse the color */ + if( edit_policy ) { + if( cptr != NULL ) { + reg_color = cptr[0]; /* r,g,b,y,w */ + if( reg_color != 'r' && reg_color != 'g' && + reg_color != 'b' && reg_color != 'y' && reg_color != 'w' ) { + reg_color = -1; /* invalid color */ + } + + /** TBD: Get the value of reg_blink from cptr */ + } + } + else { + /* scan index for some users */ + if( !strcmp(user, Led_user_arr[EV_DISK_SMART]) || + !strcmp(user, Led_user_arr[EV_DISK_IO]) ) { + if( cptr != NULL ) { + for( i = 0; (i < INDEX_LEN -1) && cptr[i] ; i++ ) { + if( (cptr[i] == ',') || (cptr[i] == '\0') || (cptr[i] == '\n') ) { + break; + } + index_str[i] = cptr[i]; + } + } + } + + /* null terminate index_str */ + index_str[i] = '\0'; + if( i ) { + tmp = strict_strtol(index_str, 10, &index); + if( !tmp && (index >= 0) ) { + /* + * TODO: insert code to fulfill req's. Currently not required. + */ + /*printk(KERN_INFO "\nindex %ld\n", index);*/ + } + } + } /* if( !edit_policy ) */ + + /* Validate user and event */ + found = 0; + for( i = 0; i < N_USERS; i++ ) { + if( !strcmp( Led_user_arr[i], user ) ) { + found = 1; + break; } + } - led_set_color(led_cdev, state); + if( found || edit_policy) { + reg_user = i; + /* new user registration */ + if( ! found ) { + if( N_USERS == MAX_USERS ) { + /* only support up to 32 users */ + return (ssize_t)size; + } + reg_user = N_USERS++; + + strcpy(Led_user_arr[reg_user], user); + } + found = 0; + for( j = 0; j < N_EVENTS; j++ ) { + if( ! strcmp(Led_ev_arr[j], event) ) { + if( j == LED_STAT_ERR ) { + Led_error_bits |= (1 << i); /* register error for this user */ + } + else if( j == LED_STAT_OK ) { + Led_error_bits &= ~(1 << i); /* clear error for this user */ + } + found = 1; + reg_event = j; + break; + } + } + } + + /* if this is a register event, do just that */ + if( edit_policy ) { + /* valid event above and color */ + if( (reg_event != -1) && (reg_color != -1) ) { + Color_map[reg_user][reg_event] = reg_color; + + /** TBD: Add support for registering blink with register: interface*/ + reg_blink = 'n'; + Blink_map[reg_user][reg_event] = reg_blink; + } + /*printk( KERN_INFO "reg_user = %d, reg_event= %d, reg_color = %c\n", reg_user, reg_event, reg_color, reg_blink);*/ + return (ssize_t)size; + } + + /* Be nice ! support older led mechanism */ + color = buf[0]; + blink = 'x'; + + /* If valid user and event, retrieve color & blink map */ + if( found ) { + /* if a canceling event and other error(s) existing, don't do anything */ + if( (j == LED_STAT_OK) && (Led_error_bits != 0) ) { + } + else { + color = Color_map[i][j]; + blink = Blink_map[i][j]; + } + /*printk(KERN_INFO "\nUser= %s, event= %s, color %c, %08x\n", user, event, color, blink, Led_error_bits);*/ + } + + switch (color) { + case 'r': /* red */ + state = 1; + break; + case 'g': /* green */ + state = 2; + break; + case 'b': /* blue */ + state = 3; + break; + case 'y': /* yellow */ + state = 4; + break; + case 'w': /* white */ + state = 5; + break; + case 'o': /* off */ + state = 0; + break; + default: + state = -1; + break; + } + + /** do nothing if no color change is required */ + if( state == -1 ) { + return (ssize_t)size; + } + + // printk(KERN_DEBUG "Calling led_set_color with value %c, blink is %c\n", color, blink); + led_set_color( led_cdev, state ); + + /** blink the led */ + { + int val = -1; + + printk(KERN_DEBUG "Calling led_set_blink with value %c\n", blink); + + switch( blink ) { + case 'y': /** yes */ + val = 1; + break; + case 'n': /** no */ + val = 0; + break; + case 'f': /** forced */ + val = 2; + break; + default: + break; + } + + if( val >= 0 ) + { + led_set_blink( led_cdev, val ); + } + } - return (ssize_t)size; + return (ssize_t)size; } static ssize_t led_color_show(struct device *dev, - struct device_attribute *attr, char *buf) { + struct device_attribute *attr, char *buf) { - struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); char * readbuf[] = {"off", "red", "green", "blue", "yellow", "white"} ; - /* no lock needed for this */ - led_update_color(led_cdev); + /* no lock needed for this */ + led_update_color(led_cdev); - return sprintf(buf, "%s\n", readbuf[led_cdev->color]); + return sprintf(buf, "%s\n", readbuf[led_cdev->color]); } static ssize_t led_blink_show(struct device *dev, struct device_attribute *attr, @@ -86,11 +323,11 @@ static ssize_t led_blink_show(struct device *dev, struct device_attribute *attr, else if (led_cdev->blink == 1 ){ blinkStr = "yes"; } - return sprintf(buf, "%s\n", blinkStr); + return sprintf(buf, "%s\n", blinkStr); } static ssize_t led_blink_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) { + struct device_attribute *attr, const char *buf, size_t size) { int val = 0; struct led_classdev * led_cdev = dev_get_drvdata(dev); @@ -109,11 +346,11 @@ static ssize_t led_blink_store(struct device *dev, } static ssize_t led_max_brightness_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { - struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); - return sprintf(buf, "%u\n", led_cdev->max_brightness); + return sprintf(buf, "%u\n", led_cdev->max_brightness); } /*static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);*/ @@ -130,8 +367,8 @@ static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); */ void led_classdev_suspend(struct led_classdev *led_cdev) { - led_cdev->flags |= LED_SUSPENDED; - led_cdev->color_set(led_cdev, 0); + led_cdev->flags |= LED_SUSPENDED; + led_cdev->color_set(led_cdev, 0); } EXPORT_SYMBOL_GPL(led_classdev_suspend); @@ -141,29 +378,29 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend); */ void led_classdev_resume(struct led_classdev *led_cdev) { - led_cdev->color_set(led_cdev, led_cdev->color); - led_cdev->flags &= ~LED_SUSPENDED; + led_cdev->color_set(led_cdev, led_cdev->color); + led_cdev->flags &= ~LED_SUSPENDED; } EXPORT_SYMBOL_GPL(led_classdev_resume); static int led_suspend(struct device *dev, pm_message_t state) { - struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); - if (led_cdev->flags & LED_CORE_SUSPENDRESUME) - led_classdev_suspend(led_cdev); + if (led_cdev->flags & LED_CORE_SUSPENDRESUME) + led_classdev_suspend(led_cdev); - return 0; + return 0; } static int led_resume(struct device *dev) { - struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); - if (led_cdev->flags & LED_CORE_SUSPENDRESUME) - led_classdev_resume(led_cdev); + if (led_cdev->flags & LED_CORE_SUSPENDRESUME) + led_classdev_resume(led_cdev); - return 0; + return 0; } /** @@ -173,62 +410,62 @@ static int led_resume(struct device *dev) */ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) { - int rc; + int rc; - led_cdev->dev = device_create(leds_class, parent, 0, led_cdev, - "%s", led_cdev->name); - if (IS_ERR(led_cdev->dev)) - return PTR_ERR(led_cdev->dev); + led_cdev->dev = device_create(leds_class, parent, 0, led_cdev, + "%s", led_cdev->name); + if (IS_ERR(led_cdev->dev)) + return PTR_ERR(led_cdev->dev); - /* register the attributes */ - rc = device_create_file(led_cdev->dev, &dev_attr_color); - if (rc) - goto err_out; + /* register the attributes */ + rc = device_create_file(led_cdev->dev, &dev_attr_color); + if (rc) + goto err_out; - rc = device_create_file(led_cdev->dev, &dev_attr_blink); - if (rc) - goto err_out; + rc = device_create_file(led_cdev->dev, &dev_attr_blink); + if (rc) + goto err_out; #ifdef CONFIG_LEDS_TRIGGERS - init_rwsem(&led_cdev->trigger_lock); + init_rwsem(&led_cdev->trigger_lock); #endif - /* add to the list of leds */ - down_write(&leds_list_lock); - list_add_tail(&led_cdev->node, &leds_list); - up_write(&leds_list_lock); + /* add to the list of leds */ + down_write(&leds_list_lock); + list_add_tail(&led_cdev->node, &leds_list); + up_write(&leds_list_lock); - if (!led_cdev->max_brightness) - led_cdev->max_brightness = LED_FULL; + if (!led_cdev->max_brightness) + led_cdev->max_brightness = LED_FULL; - rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness); - if (rc) - goto err_out_attr_max; + rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness); + if (rc) + goto err_out_attr_max; - led_update_color(led_cdev); + led_update_color(led_cdev); #ifdef CONFIG_LEDS_TRIGGERS - rc = device_create_file(led_cdev->dev, &dev_attr_trigger); - if (rc) - goto err_out_led_list; + rc = device_create_file(led_cdev->dev, &dev_attr_trigger); + if (rc) + goto err_out_led_list; - led_trigger_set_default(led_cdev); + led_trigger_set_default(led_cdev); #endif - printk(KERN_INFO "Registered led device: %s\n", - led_cdev->name); + printk(KERN_INFO "Registered led device: %s\n", + led_cdev->name); - return 0; + return 0; #ifdef CONFIG_LEDS_TRIGGERS err_out_led_list: - device_remove_file(led_cdev->dev, &dev_attr_max_brightness); + device_remove_file(led_cdev->dev, &dev_attr_max_brightness); #endif err_out_attr_max: - device_remove_file(led_cdev->dev, &dev_attr_color); - list_del(&led_cdev->node); + device_remove_file(led_cdev->dev, &dev_attr_color); + list_del(&led_cdev->node); err_out: - device_unregister(led_cdev->dev); - return rc; + device_unregister(led_cdev->dev); + return rc; } EXPORT_SYMBOL_GPL(led_classdev_register); @@ -240,37 +477,37 @@ EXPORT_SYMBOL_GPL(led_classdev_register); */ void led_classdev_unregister(struct led_classdev *led_cdev) { - device_remove_file(led_cdev->dev, &dev_attr_max_brightness); - device_remove_file(led_cdev->dev, &dev_attr_color); + device_remove_file(led_cdev->dev, &dev_attr_max_brightness); + device_remove_file(led_cdev->dev, &dev_attr_color); #ifdef CONFIG_LEDS_TRIGGERS - device_remove_file(led_cdev->dev, &dev_attr_trigger); - down_write(&led_cdev->trigger_lock); - if (led_cdev->trigger) - led_trigger_set(led_cdev, NULL); - up_write(&led_cdev->trigger_lock); + device_remove_file(led_cdev->dev, &dev_attr_trigger); + down_write(&led_cdev->trigger_lock); + if (led_cdev->trigger) + led_trigger_set(led_cdev, NULL); + up_write(&led_cdev->trigger_lock); #endif - device_unregister(led_cdev->dev); + device_unregister(led_cdev->dev); - down_write(&leds_list_lock); - list_del(&led_cdev->node); - up_write(&leds_list_lock); + down_write(&leds_list_lock); + list_del(&led_cdev->node); + up_write(&leds_list_lock); } EXPORT_SYMBOL_GPL(led_classdev_unregister); static int __init leds_init(void) { - leds_class = class_create(THIS_MODULE, "leds"); - if (IS_ERR(leds_class)) - return PTR_ERR(leds_class); - leds_class->suspend = led_suspend; - leds_class->resume = led_resume; - return 0; + leds_class = class_create(THIS_MODULE, "leds"); + if (IS_ERR(leds_class)) + return PTR_ERR(leds_class); + leds_class->suspend = led_suspend; + leds_class->resume = led_resume; + return 0; } static void __exit leds_exit(void) { - class_destroy(leds_class); + class_destroy(leds_class); } subsys_initcall(leds_init); diff --git a/drivers/leds/leds-apollo3g.c b/drivers/leds/leds-apollo3g.c index 6e796dc7c24..5fc791383c3 100644 --- a/drivers/leds/leds-apollo3g.c +++ b/drivers/leds/leds-apollo3g.c @@ -157,24 +157,25 @@ static int a3g_led_blink(struct led_classdev *led_cdev, int value) { /* * if forced blink, don't set blink_flag */ - if( blink_flag == 2 ) { - return 0; - } + if( blink_flag == 2 ) { + return 0; + } /*spin_lock_irqsave(&led_lock, flags);*/ /* user wants to blink led */ if( value == 1 ) { - wake_up(&ts_wait); blink_flag = 1; - + wake_up(&ts_wait); } else if( value == 0) { blink_flag = 0; } else if( value == 2 ) { - wake_up(&ts_wait); blink_flag = 2; + wake_up(&ts_wait); } + // printk(KERN_DEBUG "%s: Got blink signal - input blink value %d, blink_flag %d\n", __func__, value, blink_flag); + /* spin_unlock_irqrestore(&led_lock, flags);*/ return 0; @@ -193,12 +194,12 @@ void signal_hdd_led(int flag, int color) { if( blink_flag == 2 ) { return; } - - if( flag && /* blink or not */ + + if( flag && /* blink == yes */ (led_state.cur_color == _3G_LED_GREEN) #if 0 - (led_state.cur_color != _3G_LED_WHITE) && /* don't touch fw update led */ - (led_state.cur_color != _3G_LED_RED) && /* don't touch system error led */ + (led_state.cur_color != _3G_LED_WHITE) && /* don't touch fw update led */ + (led_state.cur_color != _3G_LED_RED) && /* don't touch system error led */ !((led_state.cur_color == _3G_LED_BLUE) && (led_state.cur_action == _BLINK_YES)) && /* leave identity alone */ (color != _3G_LED_RED) #endif @@ -209,23 +210,27 @@ void signal_hdd_led(int flag, int color) { blink_flag = 1; wake_up(&ts_wait); } - else { - blink_flag = 0; + else if( ! flag && /* blink == no */ + ( led_state.cur_color == _3G_LED_GREEN ) ) + { + blink_flag = 0; } + + //printk(KERN_DEBUG "%s: Got HDD signal - color %d, blink %d, blink_flag %d\n", __func__, color, flag, blink_flag); } static struct led_classdev a3g_led_dev = { .name = "a3g_led", - .color_set = a3g_led_set, - .color_get = a3g_led_get, - .blink_set_3g = a3g_led_blink, + .color_set = a3g_led_set, + .color_get = a3g_led_get, + .blink_set_3g = a3g_led_blink, }; /****************************************************/ static int __init a3g_led_probe(struct platform_device *pdev ) { - /* Not used */ - return 0; + /* Not used */ + return 0; } /****************************************************/ @@ -239,12 +244,12 @@ static int __devexit a3g_led_remove(struct platform_device *pdev){ return 0; } static struct platform_driver a3g_led_driver = { - .probe = a3g_led_probe, - .remove = __devexit_p(a3g_led_remove), - .driver = { - .name = "a3g-leds", + .probe = a3g_led_probe, + .remove = __devexit_p(a3g_led_remove), + .driver = { + .name = "a3g-leds", .owner = THIS_MODULE, - }, + }, }; #if 0 @@ -287,7 +292,7 @@ static int a3g_led_blink_thread( void * data ) { led_state.cur_action = _BLINK_NO; /* always set current color before blinking */ - a3g_led_set( NULL, led_state.cur_color); + a3g_led_set( NULL, led_state.cur_color); wait_event_freezable_timeout(ts_wait, blink_flag || kthread_should_stop(), MAX_SCHEDULE_TIMEOUT); if( led_port ) { readval = readb(led_port); 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_ |