|
@@ -1,4 +1,4 @@
|
|
-#define VERSION "0.22"
|
|
|
|
|
|
+#define VERSION "0.23"
|
|
/* ns83820.c by Benjamin LaHaise with contributions.
|
|
/* ns83820.c by Benjamin LaHaise with contributions.
|
|
*
|
|
*
|
|
* Questions/comments/discussion to linux-ns83820@kvack.org.
|
|
* Questions/comments/discussion to linux-ns83820@kvack.org.
|
|
@@ -1247,6 +1247,149 @@ static struct net_device_stats *ns83820_get_stats(struct net_device *ndev)
|
|
return &dev->stats;
|
|
return &dev->stats;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Let ethtool retrieve info */
|
|
|
|
+static int ns83820_get_settings(struct net_device *ndev,
|
|
|
|
+ struct ethtool_cmd *cmd)
|
|
|
|
+{
|
|
|
|
+ struct ns83820 *dev = PRIV(ndev);
|
|
|
|
+ u32 cfg, tanar, tbicr;
|
|
|
|
+ int have_optical = 0;
|
|
|
|
+ int fullduplex = 0;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Here's the list of available ethtool commands from other drivers:
|
|
|
|
+ * cmd->advertising =
|
|
|
|
+ * cmd->speed =
|
|
|
|
+ * cmd->duplex =
|
|
|
|
+ * cmd->port = 0;
|
|
|
|
+ * cmd->phy_address =
|
|
|
|
+ * cmd->transceiver = 0;
|
|
|
|
+ * cmd->autoneg =
|
|
|
|
+ * cmd->maxtxpkt = 0;
|
|
|
|
+ * cmd->maxrxpkt = 0;
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ /* read current configuration */
|
|
|
|
+ cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
|
|
|
|
+ tanar = readl(dev->base + TANAR);
|
|
|
|
+ tbicr = readl(dev->base + TBICR);
|
|
|
|
+
|
|
|
|
+ if (dev->CFG_cache & CFG_TBI_EN) {
|
|
|
|
+ /* we have an optical interface */
|
|
|
|
+ have_optical = 1;
|
|
|
|
+ fullduplex = (cfg & CFG_DUPSTS) ? 1 : 0;
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ /* We have copper */
|
|
|
|
+ fullduplex = (cfg & CFG_DUPSTS) ? 1 : 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cmd->supported = SUPPORTED_Autoneg;
|
|
|
|
+
|
|
|
|
+ /* we have optical interface */
|
|
|
|
+ if (dev->CFG_cache & CFG_TBI_EN) {
|
|
|
|
+ cmd->supported |= SUPPORTED_1000baseT_Half |
|
|
|
|
+ SUPPORTED_1000baseT_Full |
|
|
|
|
+ SUPPORTED_FIBRE;
|
|
|
|
+ cmd->port = PORT_FIBRE;
|
|
|
|
+ } /* TODO: else copper related support */
|
|
|
|
+
|
|
|
|
+ cmd->duplex = fullduplex ? DUPLEX_FULL : DUPLEX_HALF;
|
|
|
|
+ switch (cfg / CFG_SPDSTS0 & 3) {
|
|
|
|
+ case 2:
|
|
|
|
+ cmd->speed = SPEED_1000;
|
|
|
|
+ break;
|
|
|
|
+ case 1:
|
|
|
|
+ cmd->speed = SPEED_100;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ cmd->speed = SPEED_10;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ cmd->autoneg = (tbicr & TBICR_MR_AN_ENABLE) ? 1: 0;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Let ethool change settings*/
|
|
|
|
+static int ns83820_set_settings(struct net_device *ndev,
|
|
|
|
+ struct ethtool_cmd *cmd)
|
|
|
|
+{
|
|
|
|
+ struct ns83820 *dev = PRIV(ndev);
|
|
|
|
+ u32 cfg, tanar;
|
|
|
|
+ int have_optical = 0;
|
|
|
|
+ int fullduplex = 0;
|
|
|
|
+
|
|
|
|
+ /* read current configuration */
|
|
|
|
+ cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
|
|
|
|
+ tanar = readl(dev->base + TANAR);
|
|
|
|
+
|
|
|
|
+ if (dev->CFG_cache & CFG_TBI_EN) {
|
|
|
|
+ /* we have optical */
|
|
|
|
+ have_optical = 1;
|
|
|
|
+ fullduplex = (tanar & TANAR_FULL_DUP);
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ /* we have copper */
|
|
|
|
+ fullduplex = cfg & CFG_DUPSTS;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ spin_lock_irq(&dev->misc_lock);
|
|
|
|
+ spin_lock(&dev->tx_lock);
|
|
|
|
+
|
|
|
|
+ /* Set duplex */
|
|
|
|
+ if (cmd->duplex != fullduplex) {
|
|
|
|
+ if (have_optical) {
|
|
|
|
+ /*set full duplex*/
|
|
|
|
+ if (cmd->duplex == DUPLEX_FULL) {
|
|
|
|
+ /* force full duplex */
|
|
|
|
+ writel(readl(dev->base + TXCFG)
|
|
|
|
+ | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP,
|
|
|
|
+ dev->base + TXCFG);
|
|
|
|
+ writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
|
|
|
|
+ dev->base + RXCFG);
|
|
|
|
+ /* Light up full duplex LED */
|
|
|
|
+ writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT,
|
|
|
|
+ dev->base + GPIOR);
|
|
|
|
+ } else {
|
|
|
|
+ /*TODO: set half duplex */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ /*we have copper*/
|
|
|
|
+ /* TODO: Set duplex for copper cards */
|
|
|
|
+ }
|
|
|
|
+ printk(KERN_INFO "%s: Duplex set via ethtool\n",
|
|
|
|
+ ndev->name);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Set autonegotiation */
|
|
|
|
+ if (1) {
|
|
|
|
+ if (cmd->autoneg == AUTONEG_ENABLE) {
|
|
|
|
+ /* restart auto negotiation */
|
|
|
|
+ writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN,
|
|
|
|
+ dev->base + TBICR);
|
|
|
|
+ writel(TBICR_MR_AN_ENABLE, dev->base + TBICR);
|
|
|
|
+ dev->linkstate = LINK_AUTONEGOTIATE;
|
|
|
|
+
|
|
|
|
+ printk(KERN_INFO "%s: autoneg enabled via ethtool\n",
|
|
|
|
+ ndev->name);
|
|
|
|
+ } else {
|
|
|
|
+ /* disable auto negotiation */
|
|
|
|
+ writel(0x00000000, dev->base + TBICR);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ printk(KERN_INFO "%s: autoneg %s via ethtool\n", ndev->name,
|
|
|
|
+ cmd->autoneg ? "ENABLED" : "DISABLED");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ phy_intr(ndev);
|
|
|
|
+ spin_unlock(&dev->tx_lock);
|
|
|
|
+ spin_unlock_irq(&dev->misc_lock);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+/* end ethtool get/set support -df */
|
|
|
|
+
|
|
static void ns83820_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
|
|
static void ns83820_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
|
|
{
|
|
{
|
|
struct ns83820 *dev = PRIV(ndev);
|
|
struct ns83820 *dev = PRIV(ndev);
|
|
@@ -1263,8 +1406,10 @@ static u32 ns83820_get_link(struct net_device *ndev)
|
|
}
|
|
}
|
|
|
|
|
|
static const struct ethtool_ops ops = {
|
|
static const struct ethtool_ops ops = {
|
|
- .get_drvinfo = ns83820_get_drvinfo,
|
|
|
|
- .get_link = ns83820_get_link
|
|
|
|
|
|
+ .get_settings = ns83820_get_settings,
|
|
|
|
+ .set_settings = ns83820_set_settings,
|
|
|
|
+ .get_drvinfo = ns83820_get_drvinfo,
|
|
|
|
+ .get_link = ns83820_get_link
|
|
};
|
|
};
|
|
|
|
|
|
/* this function is called in irq context from the ISR */
|
|
/* this function is called in irq context from the ISR */
|