add r6040 phylib support which allows to use the ip175c switch driver on ar525w
authorFlorian Fainelli <florian@openwrt.org>
Fri, 21 May 2010 09:53:59 +0000 (09:53 +0000)
committerFlorian Fainelli <florian@openwrt.org>
Fri, 21 May 2010 09:53:59 +0000 (09:53 +0000)
SVN-Revision: 21521

target/linux/rdc/Makefile
target/linux/rdc/patches-2.6.32/014-r6040_phylib_support.patch [new file with mode: 0644]
target/linux/rdc/profiles/ar525w.mk

index 738af3f02bd8636a4a071a95a0c21951d6a8185d..391ee68f8321b596c34377ac3daada7bc12f7c63 100644 (file)
@@ -16,7 +16,7 @@ LINUX_VERSION:=2.6.32.12
 
 include $(INCLUDE_DIR)/target.mk
 
-DEFAULT_PACKAGES += wpad-mini kmod-r6040 kmod-input-core \
+DEFAULT_PACKAGES += wpad-mini kmod-libphy kmod-r6040 kmod-input-core \
                    kmod-input-polldev kmod-input-gpio-buttons kmod-button-hotplug \
                    kmod-rdc321x-wdt
 
diff --git a/target/linux/rdc/patches-2.6.32/014-r6040_phylib_support.patch b/target/linux/rdc/patches-2.6.32/014-r6040_phylib_support.patch
new file mode 100644 (file)
index 0000000..9a94228
--- /dev/null
@@ -0,0 +1,468 @@
+Index: linux-2.6.32.12/drivers/net/r6040.c
+===================================================================
+--- linux-2.6.32.12.orig/drivers/net/r6040.c   2010-05-20 10:19:41.000000000 +0200
++++ linux-2.6.32.12/drivers/net/r6040.c        2010-05-20 10:28:48.000000000 +0200
+@@ -45,6 +45,7 @@
+ #include <linux/io.h>
+ #include <linux/irq.h>
+ #include <linux/uaccess.h>
++#include <linux/phy.h>
+ #include <asm/processor.h>
+@@ -180,7 +181,6 @@
+ struct r6040_private {
+       spinlock_t lock;                /* driver lock */
+-      struct timer_list timer;
+       struct pci_dev *pdev;
+       struct r6040_descriptor *rx_insert_ptr;
+       struct r6040_descriptor *rx_remove_ptr;
+@@ -190,13 +190,15 @@
+       struct r6040_descriptor *tx_ring;
+       dma_addr_t rx_ring_dma;
+       dma_addr_t tx_ring_dma;
+-      u16     tx_free_desc, phy_addr, phy_mode;
++      u16     tx_free_desc, phy_addr;
+       u16     mcr0, mcr1;
+-      u16     switch_sig;
+       struct net_device *dev;
+-      struct mii_if_info mii_if;
++      struct mii_bus *mii_bus;
+       struct napi_struct napi;
+       void __iomem *base;
++      struct phy_device *phydev;
++      int old_link;
++      int old_duplex;
+ };
+ static char version[] __devinitdata = KERN_INFO DRV_NAME
+@@ -239,20 +241,29 @@
+       }
+ }
+-static int r6040_mdio_read(struct net_device *dev, int mii_id, int reg)
++static int r6040_mdiobus_read(struct mii_bus *bus, int phy_addr, int reg)
+ {
++      struct net_device *dev = bus->priv;
+       struct r6040_private *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
+-      return (r6040_phy_read(ioaddr, lp->phy_addr, reg));
++      return r6040_phy_read(ioaddr, phy_addr, reg);
+ }
+-static void r6040_mdio_write(struct net_device *dev, int mii_id, int reg, int val)
++static int r6040_mdiobus_write(struct mii_bus *bus, int phy_addr, int reg, u16 value)
+ {
++      struct net_device *dev = bus->priv;
+       struct r6040_private *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
+-      r6040_phy_write(ioaddr, lp->phy_addr, reg, val);
++      r6040_phy_write(ioaddr, phy_addr, reg, value);
++
++      return 0;
++}
++
++static int r6040_mdiobus_reset(struct mii_bus *bus)
++{
++      return 0;
+ }
+ static void r6040_free_txbufs(struct net_device *dev)
+@@ -409,10 +420,9 @@
+       void __iomem *ioaddr = priv->base;
+       printk(KERN_WARNING "%s: transmit timed out, int enable %4.4x "
+-              "status %4.4x, PHY status %4.4x\n",
++              "status %4.4x\n",
+               dev->name, ioread16(ioaddr + MIER),
+-              ioread16(ioaddr + MISR),
+-              r6040_mdio_read(dev, priv->mii_if.phy_id, MII_BMSR));
++              ioread16(ioaddr + MISR));
+       dev->stats.tx_errors++;
+@@ -464,9 +474,6 @@
+       struct r6040_private *lp = netdev_priv(dev);
+       struct pci_dev *pdev = lp->pdev;
+-      /* deleted timer */
+-      del_timer_sync(&lp->timer);
+-
+       spin_lock_irq(&lp->lock);
+       napi_disable(&lp->napi);
+       netif_stop_queue(dev);
+@@ -496,64 +503,14 @@
+       return 0;
+ }
+-/* Status of PHY CHIP */
+-static int r6040_phy_mode_chk(struct net_device *dev)
+-{
+-      struct r6040_private *lp = netdev_priv(dev);
+-      void __iomem *ioaddr = lp->base;
+-      int phy_dat;
+-
+-      /* PHY Link Status Check */
+-      phy_dat = r6040_phy_read(ioaddr, lp->phy_addr, 1);
+-      if (!(phy_dat & 0x4))
+-              phy_dat = 0x8000;       /* Link Failed, full duplex */
+-
+-      /* PHY Chip Auto-Negotiation Status */
+-      phy_dat = r6040_phy_read(ioaddr, lp->phy_addr, 1);
+-      if (phy_dat & 0x0020) {
+-              /* Auto Negotiation Mode */
+-              phy_dat = r6040_phy_read(ioaddr, lp->phy_addr, 5);
+-              phy_dat &= r6040_phy_read(ioaddr, lp->phy_addr, 4);
+-              if (phy_dat & 0x140)
+-                      /* Force full duplex */
+-                      phy_dat = 0x8000;
+-              else
+-                      phy_dat = 0;
+-      } else {
+-              /* Force Mode */
+-              phy_dat = r6040_phy_read(ioaddr, lp->phy_addr, 0);
+-              if (phy_dat & 0x100)
+-                      phy_dat = 0x8000;
+-              else
+-                      phy_dat = 0x0000;
+-      }
+-
+-      return phy_dat;
+-};
+-
+-static void r6040_set_carrier(struct mii_if_info *mii)
+-{
+-      if (r6040_phy_mode_chk(mii->dev)) {
+-              /* autoneg is off: Link is always assumed to be up */
+-              if (!netif_carrier_ok(mii->dev))
+-                      netif_carrier_on(mii->dev);
+-      } else
+-              r6040_phy_mode_chk(mii->dev);
+-}
+-
+ static int r6040_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+ {
+       struct r6040_private *lp = netdev_priv(dev);
+-      struct mii_ioctl_data *data = if_mii(rq);
+-      int rc;
+-      if (!netif_running(dev))
++      if (!lp->phydev)
+               return -EINVAL;
+-      spin_lock_irq(&lp->lock);
+-      rc = generic_mii_ioctl(&lp->mii_if, data, cmd, NULL);
+-      spin_unlock_irq(&lp->lock);
+-      r6040_set_carrier(&lp->mii_if);
+-      return rc;
++
++      return phy_mii_ioctl(lp->phydev, if_mii(rq), cmd);
+ }
+ static int r6040_rx(struct net_device *dev, int limit)
+@@ -752,26 +709,6 @@
+       if (ret)
+               return ret;
+-      /* Read the PHY ID */
+-      lp->switch_sig = r6040_phy_read(ioaddr, 0, 2);
+-
+-      if (lp->switch_sig  == ICPLUS_PHY_ID) {
+-              r6040_phy_write(ioaddr, 29, 31, 0x175C); /* Enable registers */
+-              lp->phy_mode = 0x8000;
+-      } else {
+-              /* PHY Mode Check */
+-              r6040_phy_write(ioaddr, lp->phy_addr, 4, PHY_CAP);
+-              r6040_phy_write(ioaddr, lp->phy_addr, 0, PHY_MODE);
+-
+-              if (PHY_MODE == 0x3100)
+-                      lp->phy_mode = r6040_phy_mode_chk(dev);
+-              else
+-                      lp->phy_mode = (PHY_MODE & 0x0100) ? 0x8000:0x0;
+-      }
+-
+-      /* Set duplex mode */
+-      lp->mcr0 |= lp->phy_mode;
+-
+       /* improve performance (by RDC guys) */
+       r6040_phy_write(ioaddr, 30, 17, (r6040_phy_read(ioaddr, 30, 17) | 0x4000));
+       r6040_phy_write(ioaddr, 30, 17, ~((~r6040_phy_read(ioaddr, 30, 17)) | 0x2000));
+@@ -784,35 +721,6 @@
+       return 0;
+ }
+-/*
+-  A periodic timer routine
+-      Polling PHY Chip Link Status
+-*/
+-static void r6040_timer(unsigned long data)
+-{
+-      struct net_device *dev = (struct net_device *)data;
+-      struct r6040_private *lp = netdev_priv(dev);
+-      void __iomem *ioaddr = lp->base;
+-      u16 phy_mode;
+-
+-      /* Polling PHY Chip Status */
+-      if (PHY_MODE == 0x3100)
+-              phy_mode = r6040_phy_mode_chk(dev);
+-      else
+-              phy_mode = (PHY_MODE & 0x0100) ? 0x8000:0x0;
+-
+-      if (phy_mode != lp->phy_mode) {
+-              lp->phy_mode = phy_mode;
+-              lp->mcr0 = (lp->mcr0 & 0x7fff) | phy_mode;
+-              iowrite16(lp->mcr0, ioaddr);
+-      }
+-
+-      /* Timer active again */
+-      mod_timer(&lp->timer, round_jiffies(jiffies + HZ));
+-
+-      /* Check media */
+-      mii_check_media(&lp->mii_if, 1, 1);
+-}
+ /* Read/set MAC address routines */
+ static void r6040_mac_address(struct net_device *dev)
+@@ -874,10 +782,6 @@
+       napi_enable(&lp->napi);
+       netif_start_queue(dev);
+-      /* set and active a timer process */
+-      setup_timer(&lp->timer, r6040_timer, (unsigned long) dev);
+-      if (lp->switch_sig != ICPLUS_PHY_ID)
+-              mod_timer(&lp->timer, jiffies + HZ);
+       return 0;
+ }
+@@ -1020,40 +924,22 @@
+ static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+ {
+       struct r6040_private *rp = netdev_priv(dev);
+-      int rc;
+-
+-      spin_lock_irq(&rp->lock);
+-      rc = mii_ethtool_gset(&rp->mii_if, cmd);
+-      spin_unlock_irq(&rp->lock);
+-      return rc;
++      return  phy_ethtool_gset(rp->phydev, cmd);
+ }
+ static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+ {
+       struct r6040_private *rp = netdev_priv(dev);
+-      int rc;
+-
+-      spin_lock_irq(&rp->lock);
+-      rc = mii_ethtool_sset(&rp->mii_if, cmd);
+-      spin_unlock_irq(&rp->lock);
+-      r6040_set_carrier(&rp->mii_if);
+-
+-      return rc;
+-}
+-
+-static u32 netdev_get_link(struct net_device *dev)
+-{
+-      struct r6040_private *rp = netdev_priv(dev);
+-      return mii_link_ok(&rp->mii_if);
++      return phy_ethtool_sset(rp->phydev, cmd);
+ }
+ static const struct ethtool_ops netdev_ethtool_ops = {
+       .get_drvinfo            = netdev_get_drvinfo,
+       .get_settings           = netdev_get_settings,
+       .set_settings           = netdev_set_settings,
+-      .get_link               = netdev_get_link,
++      .get_link               = ethtool_op_get_link,
+ };
+ static const struct net_device_ops r6040_netdev_ops = {
+@@ -1072,6 +958,86 @@
+ #endif
+ };
++static void r6040_adjust_link(struct net_device *dev)
++{
++      struct r6040_private *lp = netdev_priv(dev);
++      struct phy_device *phydev = lp->phydev;
++      int status_changed = 0;
++      void __iomem *ioaddr = lp->base;
++
++      BUG_ON (!phydev);
++
++      if (lp->old_link != phydev->link) {
++              status_changed = 1;
++              lp->old_link = phydev->link;
++      }
++
++      /* reflect duplex change */
++      if (phydev->link && (lp->old_duplex != phydev->duplex)) {
++              lp->mcr0 |= (phydev->duplex == DUPLEX_FULL ? 0x8000 : 0);
++              iowrite16(lp->mcr0, ioaddr);
++
++              status_changed = 1;
++              lp->old_duplex = phydev->duplex;
++      }
++
++      if (status_changed) {
++              pr_info("%s: link %s", dev->name, phydev->link ?
++                      "UP" : "DOWN");
++              if (phydev->link)
++                      pr_cont(" - %d/%s", phydev->speed,
++                               DUPLEX_FULL == phydev->duplex ? "full" : "half");
++              pr_cont("\n");
++      }
++}
++
++static int r6040_mii_probe(struct net_device *dev)
++{
++      struct r6040_private *lp = netdev_priv(dev);
++      struct phy_device *phydev = NULL;
++      int phy_addr;
++
++      for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
++              if (lp->mii_bus->phy_map[phy_addr]) {
++                      phydev = lp->mii_bus->phy_map[phy_addr];
++                      break;
++              }
++      }
++
++      if (!phydev) {
++              printk(KERN_ERR DRV_NAME "no PHY found\n");
++              return -ENODEV;
++      }
++
++      phydev = phy_connect(dev, dev_name(&phydev->dev), &r6040_adjust_link,
++                              0, PHY_INTERFACE_MODE_MII);
++
++      if (IS_ERR(phydev)) {
++              printk(KERN_ERR DRV_NAME "could not attach to PHY\n");
++              return PTR_ERR(phydev);
++      }
++
++      /* mask with MAC supported features */
++      phydev->supported &= (SUPPORTED_10baseT_Half
++                              | SUPPORTED_10baseT_Full
++                              | SUPPORTED_100baseT_Half
++                              | SUPPORTED_100baseT_Full
++                              | SUPPORTED_Autoneg
++                              | SUPPORTED_MII
++                              | SUPPORTED_TP);
++
++      phydev->advertising = phydev->supported;
++      lp->phydev = phydev;
++      lp->old_link = 0;
++      lp->old_duplex = -1;
++
++        printk(KERN_INFO "%s: attached PHY driver [%s] "
++               "(mii_bus:phy_addr=%s)\n", dev->name,
++               phydev->drv->name, dev_name(&phydev->dev));
++
++      return 0;
++}
++
+ static int __devinit r6040_init_one(struct pci_dev *pdev,
+                                        const struct pci_device_id *ent)
+ {
+@@ -1082,6 +1048,7 @@
+       static int card_idx = -1;
+       int bar = 0;
+       u16 *adrp;
++      int i;
+       printk("%s\n", version);
+@@ -1169,7 +1136,6 @@
+       /* Init RDC private data */
+       lp->mcr0 = 0x1002;
+       lp->phy_addr = phy_table[card_idx];
+-      lp->switch_sig = 0;
+       /* The RDC-specific entries in the device structure. */
+       dev->netdev_ops = &r6040_netdev_ops;
+@@ -1177,28 +1143,61 @@
+       dev->watchdog_timeo = TX_TIMEOUT;
+       netif_napi_add(dev, &lp->napi, r6040_poll, 64);
+-      lp->mii_if.dev = dev;
+-      lp->mii_if.mdio_read = r6040_mdio_read;
+-      lp->mii_if.mdio_write = r6040_mdio_write;
+-      lp->mii_if.phy_id = lp->phy_addr;
+-      lp->mii_if.phy_id_mask = 0x1f;
+-      lp->mii_if.reg_num_mask = 0x1f;
++
++      lp->mii_bus = mdiobus_alloc();
++      if (!lp->mii_bus) {
++              printk(KERN_ERR DRV_NAME ": mdiobus_alloc failed\n");
++              goto err_out_unmap;
++      }
++
++      lp->mii_bus->priv = dev;
++      lp->mii_bus->read = r6040_mdiobus_read;
++      lp->mii_bus->write = r6040_mdiobus_write;
++      lp->mii_bus->reset = r6040_mdiobus_reset;
++      lp->mii_bus->name = "r6040_eth_mii";
++      snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%x", card_idx);
++      lp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
++      if (!lp->mii_bus->irq) {
++              printk(KERN_ERR DRV_NAME ": allocation failed\n");
++              goto err_out_mdio;
++      }
++
++      for (i = 0; i < PHY_MAX_ADDR; i++)
++              lp->mii_bus->irq[i] = PHY_POLL;
++
++      err = mdiobus_register(lp->mii_bus);
++      if (err) {
++              printk(KERN_ERR DRV_NAME ": failed to register MII bus\n");
++              goto err_out_mdio_irq;
++      }
++
++      err = r6040_mii_probe(dev);
++      if (err) {
++              printk(KERN_ERR DRV_NAME ": failed to probe MII bus\n");
++              goto err_out_mdio_unregister;
++      }
+       /* Check the vendor ID on the PHY, if 0xffff assume none attached */
+       if (r6040_phy_read(ioaddr, lp->phy_addr, 2) == 0xffff) {
+               printk(KERN_ERR DRV_NAME ": Failed to detect an attached PHY\n");
+               err = -ENODEV;
+-              goto err_out_unmap;
++              goto err_out_mdio_unregister;
+       }
+       /* Register net device. After this dev->name assign */
+       err = register_netdev(dev);
+       if (err) {
+               printk(KERN_ERR DRV_NAME ": Failed to register net device\n");
+-              goto err_out_unmap;
++              goto err_out_mdio_unregister;
+       }
+       return 0;
++err_out_mdio_unregister:
++      mdiobus_unregister(lp->mii_bus);
++err_out_mdio_irq:
++      kfree(lp->mii_bus->irq);
++err_out_mdio:
++      mdiobus_free(lp->mii_bus);
+ err_out_unmap:
+       pci_iounmap(pdev, ioaddr);
+ err_out_free_res:
+@@ -1212,8 +1211,12 @@
+ static void __devexit r6040_remove_one(struct pci_dev *pdev)
+ {
+       struct net_device *dev = pci_get_drvdata(pdev);
++      struct r6040_private *lp = netdev_priv(dev);
+       unregister_netdev(dev);
++      mdiobus_unregister(lp->mii_bus);
++      kfree(lp->mii_bus->irq);
++      mdiobus_free(lp->mii_bus);
+       pci_release_regions(pdev);
+       free_netdev(dev);
+       pci_disable_device(pdev);
index c2faee9da6afd71d396f181981828a137442d651..e86f47a49af1a5f24ca35dcca5fbd1f0bfd75a3e 100644 (file)
@@ -6,7 +6,7 @@
 #
 
 define Profile/ar525w
-       NAME:=Airlink AR525W
-       PACKAGES:=kmod-rt61-pci
+  NAME:=Airlink AR525W
+  PACKAGES:=kmod-rt61-pci kmod-switch-ip175c
 endef
 $(eval $(call Profile,ar525w))