net/ps3: Add support for gelic link negotiation
authorHideyuki Sasaki <Hideyuki_Sasaki@hq.scei.sony.co.jp>
Tue, 1 Dec 2009 12:15:58 +0000 (12:15 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 2 Dec 2009 08:52:31 +0000 (00:52 -0800)
Add ethtool_ops.set_settings support to the PS3 gelic network driver.
Allows manual setting of ethernet link speed.

Signed-off-by: Hideyuki Sasaki <xhide@rd.scei.sony.co.jp>
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ps3_gelic_net.c
drivers/net/ps3_gelic_net.h

index 06a29768a8647160b29116c8dcaf72f5f2044d57..89c4948300a52dafc0fd7110ae1fbb0fd3ac0e3e 100644 (file)
@@ -107,6 +107,24 @@ static void gelic_card_get_ether_port_status(struct gelic_card *card,
        }
 }
 
+static int gelic_card_set_link_mode(struct gelic_card *card, int mode)
+{
+       int status;
+       u64 v1, v2;
+
+       status = lv1_net_control(bus_id(card), dev_id(card),
+                                GELIC_LV1_SET_NEGOTIATION_MODE,
+                                GELIC_LV1_PHY_ETHERNET_0, mode, 0, &v1, &v2);
+       if (status) {
+               pr_info("%s: failed setting negotiation mode %d\n", __func__,
+                       status);
+               return -EBUSY;
+       }
+
+       card->link_mode = mode;
+       return 0;
+}
+
 void gelic_card_up(struct gelic_card *card)
 {
        pr_debug("%s: called\n", __func__);
@@ -1244,14 +1262,58 @@ static int gelic_ether_get_settings(struct net_device *netdev,
        cmd->supported = SUPPORTED_TP | SUPPORTED_Autoneg |
                        SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
                        SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
-                       SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full;
+                       SUPPORTED_1000baseT_Full;
        cmd->advertising = cmd->supported;
-       cmd->autoneg = AUTONEG_ENABLE; /* always enabled */
+       if (card->link_mode & GELIC_LV1_ETHER_AUTO_NEG) {
+               cmd->autoneg = AUTONEG_ENABLE;
+       } else {
+               cmd->autoneg = AUTONEG_DISABLE;
+               cmd->advertising &= ~ADVERTISED_Autoneg;
+       }
        cmd->port = PORT_TP;
 
        return 0;
 }
 
+static int gelic_ether_set_settings(struct net_device *netdev,
+                                   struct ethtool_cmd *cmd)
+{
+       struct gelic_card *card = netdev_card(netdev);
+       u64 mode;
+       int ret;
+
+       if (cmd->autoneg == AUTONEG_ENABLE) {
+               mode = GELIC_LV1_ETHER_AUTO_NEG;
+       } else {
+               switch (cmd->speed) {
+               case SPEED_10:
+                       mode = GELIC_LV1_ETHER_SPEED_10;
+                       break;
+               case SPEED_100:
+                       mode = GELIC_LV1_ETHER_SPEED_100;
+                       break;
+               case SPEED_1000:
+                       mode = GELIC_LV1_ETHER_SPEED_1000;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               if (cmd->duplex == DUPLEX_FULL)
+                       mode |= GELIC_LV1_ETHER_FULL_DUPLEX;
+               else if (cmd->speed == SPEED_1000) {
+                       pr_info("1000 half duplex is not supported.\n");
+                       return -EINVAL;
+               }
+       }
+
+       ret = gelic_card_set_link_mode(card, mode);
+
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
 u32 gelic_net_get_rx_csum(struct net_device *netdev)
 {
        struct gelic_card *card = netdev_card(netdev);
@@ -1349,6 +1411,7 @@ done:
 static const struct ethtool_ops gelic_ether_ethtool_ops = {
        .get_drvinfo    = gelic_net_get_drvinfo,
        .get_settings   = gelic_ether_get_settings,
+       .set_settings   = gelic_ether_set_settings,
        .get_link       = ethtool_op_get_link,
        .get_tx_csum    = ethtool_op_get_tx_csum,
        .set_tx_csum    = ethtool_op_set_tx_csum,
@@ -1657,6 +1720,8 @@ static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
        /* get internal vlan info */
        gelic_card_get_vlan_info(card);
 
+       card->link_mode = GELIC_LV1_ETHER_AUTO_NEG;
+
        /* setup interrupt */
        result = lv1_net_set_interrupt_status_indicator(bus_id(card),
                                                        dev_id(card),
@@ -1773,6 +1838,9 @@ static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev)
        struct net_device *netdev0;
        pr_debug("%s: called\n", __func__);
 
+       /* set auto-negotiation */
+       gelic_card_set_link_mode(card, GELIC_LV1_ETHER_AUTO_NEG);
+
 #ifdef CONFIG_GELIC_WIRELESS
        gelic_wl_driver_remove(card);
 #endif
index a09e4862706a82d376c770ec8c952245f6f29e52..32521ae5e8245ca54be3501e9e3f2b162380ca2e 100644 (file)
@@ -307,6 +307,8 @@ struct gelic_card {
        atomic_t users;
 
        u64 ether_port_status;
+       int link_mode;
+
        /* original address returned by kzalloc */
        void *unalign;