linux: some more patches for the ts72xx. Closes 3906.
authorPetr Stetiar <ynezz@true.cz>
Sat, 17 May 2008 02:12:51 +0000 (02:12 +0000)
committerRolf Leggewie <oe-devel@rolf.leggewie.biz>
Sat, 17 May 2008 02:12:51 +0000 (02:12 +0000)
* commit by Laibsch and reviewed by RP

packages/linux/linux-2.6.24/ts72xx/ep93xx-eth-phylib-framework.patch [new file with mode: 0644]
packages/linux/linux-2.6.24/ts72xx/ts72xx-rs485.patch
packages/linux/linux_2.6.24.bb

diff --git a/packages/linux/linux-2.6.24/ts72xx/ep93xx-eth-phylib-framework.patch b/packages/linux/linux-2.6.24/ts72xx/ep93xx-eth-phylib-framework.patch
new file mode 100644 (file)
index 0000000..3f25f30
--- /dev/null
@@ -0,0 +1,548 @@
+Currently, the ep93xx_eth driver doesn't care about the PHY state,
+but it should, in order to tell the MAC when full duplex operation is
+required; failure to do so causes degraded performance on full duplex
+links. This patch implements proper PHY handling via the phylib framework:
+
+ - clean up ep93xx_mdio_{read,write} to conform to ep93xx manual
+ - convert ep93xx_eth driver to phylib framework
+ - set full duplex bit in configuration of MAC when FDX link detected
+ - convert to use print_mac()
+
+Signed-off-by: Herbert Valerio Riedel <hvr@gnu.org>
+Cc: Lennert Buytenhek <buytenh@wantstofly.org>
+Cc: Andy Fleming <afleming@freescale.com>
+
+ drivers/net/arm/Kconfig      |    1 +
+ drivers/net/arm/ep93xx_eth.c |  354 +++++++++++++++++++++++++++++++++---------
+ 2 files changed, 282 insertions(+), 73 deletions(-)
+
+diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig
+index f9cc2b6..5860175 100644
+--- a/drivers/net/arm/Kconfig
++++ b/drivers/net/arm/Kconfig
+@@ -44,6 +44,7 @@ config EP93XX_ETH
+       tristate "EP93xx Ethernet support"
+       depends on ARM && ARCH_EP93XX
+       select MII
++      select PHYLIB
+       help
+         This is a driver for the ethernet hardware included in EP93xx CPUs.
+         Say Y if you are building a kernel for EP93xx based devices.
+diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c
+index 91a6590..6b9ac41 100644
+--- a/drivers/net/arm/ep93xx_eth.c
++++ b/drivers/net/arm/ep93xx_eth.c
+@@ -2,6 +2,7 @@
+  * EP93xx ethernet network device driver
+  * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+  * Dedicated to Marija Kulikova.
++ * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org>
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+@@ -14,6 +15,7 @@
+ #include <linux/kernel.h>
+ #include <linux/netdevice.h>
+ #include <linux/mii.h>
++#include <linux/phy.h>
+ #include <linux/etherdevice.h>
+ #include <linux/ethtool.h>
+ #include <linux/init.h>
+@@ -37,6 +39,8 @@
+ #define  REG_RXCTL_DEFAULT    0x00073800
+ #define REG_TXCTL             0x0004
+ #define  REG_TXCTL_ENABLE     0x00000001
++#define REG_TESTCTL           0x0008
++#define  REG_TESTCTL_MFDX     0x00000040
+ #define REG_MIICMD            0x0010
+ #define  REG_MIICMD_READ      0x00008000
+ #define  REG_MIICMD_WRITE     0x00004000
+@@ -45,6 +49,9 @@
+ #define  REG_MIISTS_BUSY      0x00000001
+ #define REG_SELFCTL           0x0020
+ #define  REG_SELFCTL_RESET    0x00000001
++#define  REG_SELFCTL_MDCDIV_MSK 0x00007e00
++#define  REG_SELFCTL_MDCDIV_OFS 9
++#define  REG_SELFCTL_PSPRS    0x00000100
+ #define REG_INTEN             0x0024
+ #define  REG_INTEN_TX         0x00000008
+ #define  REG_INTEN_RX         0x00000007
+@@ -174,8 +181,14 @@ struct ep93xx_priv
+       struct net_device_stats stats;
+-      struct mii_if_info      mii;
+       u8                      mdc_divisor;
++      int                     phy_supports_mfps:1;
++
++      struct mii_bus          mii_bus;
++      struct phy_device       *phy_dev;
++      int                     speed;
++      int                     duplex;
++      int                     link;
+ };
+ #define rdb(ep, off)          __raw_readb((ep)->base_addr + (off))
+@@ -185,8 +198,6 @@ struct ep93xx_priv
+ #define wrw(ep, off, val)     __raw_writew((val), (ep)->base_addr + (off))
+ #define wrl(ep, off, val)     __raw_writel((val), (ep)->base_addr + (off))
+-static int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg);
+-
+ static struct net_device_stats *ep93xx_get_stats(struct net_device *dev)
+ {
+       struct ep93xx_priv *ep = netdev_priv(dev);
+@@ -542,12 +553,6 @@ static int ep93xx_start_hw(struct net_device *dev)
+               return 1;
+       }
+-      wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9));
+-
+-      /* Does the PHY support preamble suppress?  */
+-      if ((ep93xx_mdio_read(dev, ep->mii.phy_id, MII_BMSR) & 0x0040) != 0)
+-              wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9) | (1 << 8));
+-
+       /* Receive descriptor ring.  */
+       addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rdesc);
+       wrl(ep, REG_RXDQBADD, addr);
+@@ -631,12 +636,11 @@ static int ep93xx_open(struct net_device *dev)
+               return -ENOMEM;
+       if (is_zero_ether_addr(dev->dev_addr)) {
++              DECLARE_MAC_BUF(mac_buf);
++
+               random_ether_addr(dev->dev_addr);
+-              printk(KERN_INFO "%s: generated random MAC address "
+-                      "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n", dev->name,
+-                      dev->dev_addr[0], dev->dev_addr[1],
+-                      dev->dev_addr[2], dev->dev_addr[3],
+-                      dev->dev_addr[4], dev->dev_addr[5]);
++              dev_info(&dev->dev, "generated random MAC address %s\n",
++                       print_mac(mac_buf, dev->dev_addr));
+       }
+       napi_enable(&ep->napi);
+@@ -664,6 +668,8 @@ static int ep93xx_open(struct net_device *dev)
+       wrl(ep, REG_GIINTMSK, REG_GIINTMSK_ENABLE);
++      phy_start(ep->phy_dev);
++
+       netif_start_queue(dev);
+       return 0;
+@@ -676,6 +682,9 @@ static int ep93xx_close(struct net_device *dev)
+       napi_disable(&ep->napi);
+       netif_stop_queue(dev);
++      if (ep->phy_dev)
++              phy_stop(ep->phy_dev);
++
+       wrl(ep, REG_GIINTMSK, 0);
+       free_irq(ep->irq, dev);
+       ep93xx_stop_hw(dev);
+@@ -687,53 +696,103 @@ static int ep93xx_close(struct net_device *dev)
+ static int ep93xx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ {
+       struct ep93xx_priv *ep = netdev_priv(dev);
+-      struct mii_ioctl_data *data = if_mii(ifr);
+-      return generic_mii_ioctl(&ep->mii, data, cmd, NULL);
++      return phy_mii_ioctl(ep->phy_dev, if_mii(ifr), cmd);
+ }
+-static int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg)
++/* common MII transactions should take < 100 iterations */
++#define EP93XX_PHY_TIMEOUT 2000
++
++static int ep93xx_mdio_wait(struct mii_bus *bus)
+ {
+-      struct ep93xx_priv *ep = netdev_priv(dev);
+-      int data;
+-      int i;
++      struct ep93xx_priv *ep = bus->priv;
++      unsigned int timeout = EP93XX_PHY_TIMEOUT;
+-      wrl(ep, REG_MIICMD, REG_MIICMD_READ | (phy_id << 5) | reg);
++      while ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY)
++            && timeout--)
++              cpu_relax();
+-      for (i = 0; i < 10; i++) {
+-              if ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) == 0)
+-                      break;
+-              msleep(1);
++      if (timeout <= 0) {
++              dev_err(bus->dev, "MII operation timed out\n");
++              return -ETIMEDOUT;
+       }
+-      if (i == 10) {
+-              printk(KERN_INFO DRV_MODULE_NAME ": mdio read timed out\n");
+-              data = 0xffff;
+-      } else {
+-              data = rdl(ep, REG_MIIDATA);
+-      }
++      return 0;
++}
++
++
++static int ep93xx_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
++{
++      struct ep93xx_priv *ep = bus->priv;
++      u32 selfctl;
++      u32 data;
++
++      if (ep93xx_mdio_wait(bus) < 0)
++              return -ETIMEDOUT;
++
++      selfctl = rdl(ep, REG_SELFCTL);
++
++      if (ep->phy_supports_mfps)
++        wrl(ep, REG_SELFCTL, selfctl | REG_SELFCTL_PSPRS);
++      else
++        wrl(ep, REG_SELFCTL, selfctl & ~REG_SELFCTL_PSPRS);
++
++      wrl(ep, REG_MIICMD, REG_MIICMD_READ | (mii_id << 5) | regnum);
++
++      if (ep93xx_mdio_wait(bus) < 0)
++              return -ETIMEDOUT;
++
++      data =  rdl(ep, REG_MIIDATA);
++
++      wrl(ep, REG_SELFCTL, selfctl);
+       return data;
+ }
+-static void ep93xx_mdio_write(struct net_device *dev, int phy_id, int reg, int data)
++static int ep93xx_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
++                           u16 value)
+ {
+-      struct ep93xx_priv *ep = netdev_priv(dev);
+-      int i;
++      struct ep93xx_priv *ep = bus->priv;
++      u32 selfctl;
+-      wrl(ep, REG_MIIDATA, data);
+-      wrl(ep, REG_MIICMD, REG_MIICMD_WRITE | (phy_id << 5) | reg);
++      if (ep93xx_mdio_wait(bus) < 0)
++              return -ETIMEDOUT;
+-      for (i = 0; i < 10; i++) {
+-              if ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) == 0)
+-                      break;
+-              msleep(1);
+-      }
++      selfctl = rdl(ep, REG_SELFCTL);
+-      if (i == 10)
+-              printk(KERN_INFO DRV_MODULE_NAME ": mdio write timed out\n");
++      if (ep->phy_supports_mfps)
++        wrl(ep, REG_SELFCTL, selfctl | REG_SELFCTL_PSPRS);
++      else
++        wrl(ep, REG_SELFCTL, selfctl & ~REG_SELFCTL_PSPRS);
++
++      wrl(ep, REG_MIIDATA, value);
++      wrl(ep, REG_MIICMD, REG_MIICMD_WRITE | (mii_id << 5) | regnum);
++
++      if (ep93xx_mdio_wait(bus) < 0)
++              return -ETIMEDOUT;
++
++      wrl(ep, REG_SELFCTL, selfctl);
++
++      return 0;
++}
++
++static int ep93xx_mdio_reset(struct mii_bus *bus)
++{
++      struct ep93xx_priv *ep = bus->priv;
++
++      u32 selfctl = rdl(ep, REG_SELFCTL);
++
++      selfctl &= ~(REG_SELFCTL_MDCDIV_MSK | REG_SELFCTL_PSPRS);
++
++      selfctl |= (ep->mdc_divisor - 1) << REG_SELFCTL_MDCDIV_OFS;
++      selfctl |= REG_SELFCTL_PSPRS;
++
++      wrl(ep, REG_SELFCTL, selfctl);
++
++      return 0;
+ }
++
+ static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+ {
+       strcpy(info->driver, DRV_MODULE_NAME);
+@@ -743,33 +802,30 @@ static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *i
+ static int ep93xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+ {
+       struct ep93xx_priv *ep = netdev_priv(dev);
+-      return mii_ethtool_gset(&ep->mii, cmd);
++      struct phy_device *phydev = ep->phy_dev;
++
++      if (!phydev)
++              return -ENODEV;
++
++      return phy_ethtool_gset(phydev, cmd);
+ }
+ static int ep93xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+ {
+       struct ep93xx_priv *ep = netdev_priv(dev);
+-      return mii_ethtool_sset(&ep->mii, cmd);
+-}
++      struct phy_device *phydev = ep->phy_dev;
+-static int ep93xx_nway_reset(struct net_device *dev)
+-{
+-      struct ep93xx_priv *ep = netdev_priv(dev);
+-      return mii_nway_restart(&ep->mii);
+-}
++      if (!phydev)
++              return -ENODEV;
+-static u32 ep93xx_get_link(struct net_device *dev)
+-{
+-      struct ep93xx_priv *ep = netdev_priv(dev);
+-      return mii_link_ok(&ep->mii);
++      return phy_ethtool_sset(phydev, cmd);
+ }
+ static struct ethtool_ops ep93xx_ethtool_ops = {
+       .get_drvinfo            = ep93xx_get_drvinfo,
+       .get_settings           = ep93xx_get_settings,
+       .set_settings           = ep93xx_set_settings,
+-      .nway_reset             = ep93xx_nway_reset,
+-      .get_link               = ep93xx_get_link,
++      .get_link               = ethtool_op_get_link,
+ };
+ struct net_device *ep93xx_dev_alloc(struct ep93xx_eth_data *data)
+@@ -824,15 +880,127 @@ static int ep93xx_eth_remove(struct platform_device *pdev)
+       return 0;
+ }
++static void ep93xx_adjust_link(struct net_device *dev)
++{
++      struct ep93xx_priv *ep = netdev_priv(dev);
++      struct phy_device *phydev = ep->phy_dev;
++
++      int status_change = 0;
++
++      if (phydev->link) {
++              if ((ep->speed != phydev->speed) ||
++                  (ep->duplex != phydev->duplex)) {
++                      /* speed and/or duplex state changed */
++                      u32 testctl = rdl(ep, REG_TESTCTL);
++
++                      if (DUPLEX_FULL == phydev->duplex)
++                              testctl |= REG_TESTCTL_MFDX;
++                      else
++                              testctl &= ~(REG_TESTCTL_MFDX);
++
++                      wrl(ep, REG_TESTCTL, testctl);
++
++                      ep->speed = phydev->speed;
++                      ep->duplex = phydev->duplex;
++                      status_change = 1;
++              }
++      }
++
++      /* test for online/offline link transition */
++      if (phydev->link != ep->link) {
++              if (phydev->link) /* link went online */
++                      netif_schedule(dev);
++              else { /* link went offline */
++                      ep->speed = 0;
++                      ep->duplex = -1;
++              }
++              ep->link = phydev->link;
++
++              status_change = 1;
++      }
++
++      if (status_change)
++              phy_print_status(phydev);
++}
++
++static int ep93xx_mii_probe(struct net_device *dev, int phy_addr)
++{
++      struct ep93xx_priv *ep = netdev_priv(dev);
++      struct phy_device *phydev = NULL;
++      int val;
++
++      if (phy_addr >= 0 && phy_addr < PHY_MAX_ADDR)
++              phydev = ep->mii_bus.phy_map[phy_addr];
++
++      if (!phydev) {
++              dev_info(&dev->dev,
++                       "PHY not found at specified address,"
++                       " trying autodetection\n");
++
++              /* find the first phy */
++              for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
++                      if (ep->mii_bus.phy_map[phy_addr]) {
++                              phydev = ep->mii_bus.phy_map[phy_addr];
++                              break;
++                      }
++              }
++      }
++
++      if (!phydev) {
++              dev_err(&dev->dev, "no PHY found\n");
++              return -ENODEV;
++      }
++
++      phydev = phy_connect(dev, phydev->dev.bus_id,
++                           ep93xx_adjust_link, 0, PHY_INTERFACE_MODE_MII);
++
++      if (IS_ERR(phydev)) {
++              dev_err(&dev->dev, "Could not attach to PHY\n");
++              return PTR_ERR(phydev);
++      }
++
++      ep->phy_supports_mfps = 0;
++
++      val = phy_read(phydev, MII_BMSR);
++      if (val < 0) {
++              dev_err(&phydev->dev, "failed to read MII register\n");
++              return val;
++      }
++
++      if (val & 0x0040) {
++              dev_info(&phydev->dev,
++                       "PHY supports MII frame preamble suppression\n");
++              ep->phy_supports_mfps = 1;
++      }
++
++      phydev->supported &= PHY_BASIC_FEATURES;
++
++      phydev->advertising = phydev->supported;
++
++      ep->link = 0;
++      ep->speed = 0;
++      ep->duplex = -1;
++      ep->phy_dev = phydev;
++
++      dev_info(&dev->dev, "attached PHY driver [%s] "
++               "(mii_bus:phy_addr=%s, irq=%d)\n",
++               phydev->drv->name, phydev->dev.bus_id, phydev->irq);
++
++      return 0;
++}
++
++
+ static int ep93xx_eth_probe(struct platform_device *pdev)
+ {
+       struct ep93xx_eth_data *data;
+       struct net_device *dev;
+       struct ep93xx_priv *ep;
+-      int err;
++      DECLARE_MAC_BUF(mac_buf);
++      int err, i;
+       if (pdev == NULL)
+               return -ENODEV;
++
+       data = pdev->dev.platform_data;
+       dev = ep93xx_dev_alloc(data);
+@@ -852,7 +1020,7 @@ static int ep93xx_eth_probe(struct platform_device *pdev)
+       if (ep->res == NULL) {
+               dev_err(&pdev->dev, "Could not reserve memory region\n");
+               err = -ENOMEM;
+-              goto err_out;
++              goto err_out_request_mem_region;
+       }
+       ep->base_addr = ioremap(pdev->resource[0].start,
+@@ -860,34 +1028,74 @@ static int ep93xx_eth_probe(struct platform_device *pdev)
+       if (ep->base_addr == NULL) {
+               dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
+               err = -EIO;
+-              goto err_out;
++              goto err_out_ioremap;
+       }
+       ep->irq = pdev->resource[1].start;
+-      ep->mii.phy_id = data->phy_id;
+-      ep->mii.phy_id_mask = 0x1f;
+-      ep->mii.reg_num_mask = 0x1f;
+-      ep->mii.dev = dev;
+-      ep->mii.mdio_read = ep93xx_mdio_read;
+-      ep->mii.mdio_write = ep93xx_mdio_write;
++      /* mdio/mii bus */
++      ep->mii_bus.name = "ep93xx_mii_bus";
++      ep->mii_bus.id = 0;
++
++      ep->mii_bus.read = ep93xx_mdio_read;
++      ep->mii_bus.write = ep93xx_mdio_write;
++      ep->mii_bus.reset = ep93xx_mdio_reset;
++
++      ep->mii_bus.phy_mask = 0;
++
++      ep->mii_bus.priv = ep;
++      ep->mii_bus.dev = &dev->dev;
++
++      ep->mii_bus.irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
++      if (NULL == ep->mii_bus.irq) {
++              dev_err(&pdev->dev, "Could not allocate memory\n");
++              err = -ENOMEM;
++              goto err_out_mii_bus_irq_kmalloc;
++      }
++
++      for (i = 0; i < PHY_MAX_ADDR; i++)
++              ep->mii_bus.irq[i] = PHY_POLL;
++
+       ep->mdc_divisor = 40;   /* Max HCLK 100 MHz, min MDIO clk 2.5 MHz.  */
++      ep->phy_supports_mfps = 0; /* probe without preamble suppression */
+       err = register_netdev(dev);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to register netdev\n");
+-              goto err_out;
++              goto err_out_register_netdev;
+       }
+-      printk(KERN_INFO "%s: ep93xx on-chip ethernet, IRQ %d, "
+-                       "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n", dev->name,
+-                      ep->irq, data->dev_addr[0], data->dev_addr[1],
+-                      data->dev_addr[2], data->dev_addr[3],
+-                      data->dev_addr[4], data->dev_addr[5]);
++      err = mdiobus_register(&ep->mii_bus);
++      if (err) {
++              dev_err(&dev->dev, "Could not register MII bus\n");
++              goto err_out_mdiobus_register;
++      }
++
++      err = ep93xx_mii_probe(dev, data->phy_id);
++      if (err) {
++              dev_err(&dev->dev, "failed to probe MII bus\n");
++              goto err_out_mii_probe;
++      }
++
++      dev_info(&dev->dev, "ep93xx on-chip ethernet, IRQ %d, %s\n",
++               ep->irq, print_mac(mac_buf, dev->dev_addr));
+       return 0;
++err_out_mii_probe:
++      mdiobus_unregister(&ep->mii_bus);
++err_out_mdiobus_register:
++      unregister_netdev(dev);
++err_out_register_netdev:
++      kfree(ep->mii_bus.irq);
++err_out_mii_bus_irq_kmalloc:
++      iounmap(ep->base_addr);
++err_out_ioremap:
++      release_resource(ep->res);
++      kfree(ep->res);
++err_out_request_mem_region:
++      free_netdev(dev);
+ err_out:
+-      ep93xx_eth_remove(pdev);
++
+       return err;
+ }
index 0883322..17ee397 100644 (file)
@@ -217,3 +217,222 @@ index bb9a7aa..4d7dad1 100644
  /* Used for packet mode */
  #define TIOCPKT_DATA           0
  #define TIOCPKT_FLUSHREAD      1
+RS485 auto mode support ported from 2.4 (diff against 2.6.19-rc6-git10)
+
+Signed-off-by: Petr Stetiar <ynezz@true.cz>
+
+diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c
+index 4213fab..5b3c5ff 100644
+--- a/drivers/serial/amba-pl010.c
++++ b/drivers/serial/amba-pl010.c
+@@ -50,6 +50,7 @@
+ #include <linux/amba/serial.h>
+ #include <asm/io.h>
++#include <asm/hardware.h>
+ #define UART_NR               8
+@@ -65,6 +66,11 @@
+ #define UART_DUMMY_RSR_RX     256
+ #define UART_PORT_SIZE                64
++#ifdef CONFIG_MACH_TS72XX
++static void __iomem *ts_rs485_data9_register;
++static void __iomem *ts_rs485_control_register;
++#endif
++
+ /*
+  * We wrap our port structure around the generic uart_port.
+  */
+@@ -487,6 +493,107 @@ static int pl010_verify_port(struct uart
+       return ret;
+ }
++#ifdef CONFIG_MACH_TS72XX
++static int ts72xx_rs485_init(void)
++{
++      ts_rs485_data9_register = ioremap(TS72XX_RS485_DATA9_PHYS_BASE, 4096);
++      if (ts_rs485_data9_register == NULL) {
++              return -1;
++      }
++
++      ts_rs485_control_register = ioremap(TS72XX_RS485_CONTROL_PHYS_BASE, 4096);
++      if (ts_rs485_control_register == NULL) {
++              iounmap(ts_rs485_data9_register);
++              return -1;
++      }
++
++      return 0;
++}
++
++static int ts72xx_auto485(struct uart_port *port, unsigned int cmd, unsigned long *arg)
++{
++      int baud, cflag, mode;
++      int datalength;
++
++      mode = (int)*arg;
++      if (!is_rs485_installed()) {
++              printk("amba-pl010.c: this board does not support RS485 auto mode\n");
++              return -EINVAL;
++      }
++
++      if (port->line != 1) {
++              printk("amba-pl010.c: auto RS485 mode is only supported on second port (/dev/ttyAM1)\n");
++              return -EINVAL;
++      }
++
++      datalength = 8;
++      cflag = port->info->tty->termios->c_cflag ;
++      if (cflag & PARENB)
++              datalength++;
++
++      if (cflag & CSTOPB)
++              datalength++;
++
++      baud = tty_get_baud_rate(port->info->tty);
++
++      switch (cmd) {
++              case TIOC_SBCC485:
++                      if ((mode & TS72XX_RS485_AUTO485FD) || (mode & TS72XX_RS485_AUTO485HD)) {
++                              printk("amba-pl010.c: unsetting auto RS485 mode\n");
++                              __raw_writew(TS72XX_RS485_MODE_RS232, ts_rs485_control_register);
++                              __raw_writew(TS72XX_RS485_MODE_RS232, ts_rs485_data9_register);
++                      }
++                      break;
++              case TIOC_SBCS485:
++                      if (mode & TS72XX_RS485_AUTO485FD) {
++                              printk ("amba-pl010.c: setting FULL duplex auto RS485 mode\n");
++                              __raw_writew(TS72XX_RS485_MODE_FD, ts_rs485_control_register);
++                              if (datalength > 8)
++                                      __raw_writew(TS72XX_RS485_MODE_FD, ts_rs485_data9_register);
++                      } else if (mode & TS72XX_RS485_AUTO485HD) {
++                              printk("amba-pl010.c: setting HALF DUPLEX auto RS485 mode\n");
++                              switch (baud) {
++                                      case 9600:
++                                              __raw_writew(TS72XX_RS485_MODE_9600_HD, ts_rs485_control_register);
++                                              break;
++                                      case 19200:
++                                              __raw_writew(TS72XX_RS485_MODE_19200_HD, ts_rs485_control_register);
++                                              break;
++                                      case 57600:
++                                              __raw_writew(TS72XX_RS485_MODE_57600_HD, ts_rs485_control_register);
++                                              break;
++                                      case 115200:
++                                              __raw_writew(TS72XX_RS485_MODE_115200_HD, ts_rs485_control_register);
++                                              break;
++                                      default:
++                                      printk("amba-pl010.c: %d baud rate is not supported for auto RS485 mode\n", baud);
++                                      return -1;
++                              }
++                              if (datalength > 8)
++                                      __raw_writew(TS72XX_RS485_MODE_FD, ts_rs485_data9_register);
++                      }
++                      break;
++      }
++
++      return 0;
++}
++#endif
++
++int pl010_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
++{
++#ifdef CONFIG_MACH_TS72XX
++      switch (cmd) {
++              case TIOC_SBCC485:
++              case TIOC_SBCS485:
++                      return ts72xx_auto485(port, cmd, (unsigned long *)arg);
++                      break;
++              default:
++                      return -ENOIOCTLCMD;
++      }
++#endif
++      return -ENOIOCTLCMD;
++}
++
+ static struct uart_ops amba_pl010_pops = {
+       .tx_empty       = pl010_tx_empty,
+       .set_mctrl      = pl010_set_mctrl,
+@@ -504,6 +611,7 @@ static struct uart_ops amba_pl010_pops =
+       .request_port   = pl010_request_port,
+       .config_port    = pl010_config_port,
+       .verify_port    = pl010_verify_port,
++      .ioctl          = pl010_ioctl,
+ };
+ static struct uart_amba_port *amba_ports[UART_NR];
+@@ -746,6 +854,15 @@ static int __init pl010_init(void)
+       ret = uart_register_driver(&amba_reg);
+       if (ret == 0) {
+               ret = amba_driver_register(&pl010_driver);
++#ifdef CONFIG_MACH_TS72XX
++              if (!ret && is_rs485_installed()) {
++                      ret = ts72xx_rs485_init();
++                      if (ret)
++                              printk("amba-pl010.c: ts72xx_rs485_init() failed\n");
++                      else
++                              printk("amba-pl010.c: auto RS485 mode initialized\n");
++              }
++#endif
+               if (ret)
+                       uart_unregister_driver(&amba_reg);
+       }
+@@ -756,6 +873,10 @@ static void __exit pl010_exit(void)
+ {
+       amba_driver_unregister(&pl010_driver);
+       uart_unregister_driver(&amba_reg);
++#ifdef CONFIG_MACH_TS72XX
++      iounmap(ts_rs485_data9_register);
++      iounmap(ts_rs485_control_register);
++#endif
+ }
+ module_init(pl010_init);
+diff --git a/include/asm-arm/arch-ep93xx/ts72xx.h b/include/asm-arm/arch-ep93xx/ts72xx.h
+index a94f63f..4c9396b 100644
+--- a/include/asm-arm/arch-ep93xx/ts72xx.h
++++ b/include/asm-arm/arch-ep93xx/ts72xx.h
+@@ -68,6 +68,16 @@
+ #define TS72XX_RTC_DATA_PHYS_BASE     0x11700000
+ #define TS72XX_RTC_DATA_SIZE          0x00001000
++#define TS72XX_RS485_CONTROL_PHYS_BASE        0x22C00000
++#define TS72XX_RS485_DATA9_PHYS_BASE  0x23000000
++#define TS72XX_RS485_AUTO485FD                1
++#define TS72XX_RS485_AUTO485HD                2
++#define TS72XX_RS485_MODE_RS232       0x00
++#define TS72XX_RS485_MODE_FD          0x01
++#define TS72XX_RS485_MODE_9600_HD     0x04
++#define TS72XX_RS485_MODE_19200_HD    0x05
++#define TS72XX_RS485_MODE_57600_HD    0x06
++#define TS72XX_RS485_MODE_115200_HD   0x07
+ #ifndef __ASSEMBLY__
+ #include <asm/io.h>
+@@ -87,6 +100,12 @@ static inline int board_is_ts7260(void)
+       return __raw_readb(TS72XX_MODEL_VIRT_BASE) == TS72XX_MODEL_TS7260;
+ }
++static inline int is_rs485_installed(void)
++{
++      return !!(__raw_readb(TS72XX_OPTIONS_VIRT_BASE) &
++                                      TS72XX_OPTIONS_COM2_RS485);
++}
++
+ static inline int is_max197_installed(void)
+ {
+       return !!(__raw_readb(TS72XX_OPTIONS_VIRT_BASE) &
+diff --git a/include/asm-arm/ioctls.h b/include/asm-arm/ioctls.h
+index bb9a7aa..4d7dad1 100644
+--- a/include/asm-arm/ioctls.h
++++ b/include/asm-arm/ioctls.h
+@@ -66,6 +66,9 @@
+ #define TIOCGICOUNT   0x545D  /* read serial port inline interrupt counts */
+ #define FIOQSIZE      0x545E
++#define TIOC_SBCC485  0x545F /* TS72xx RTS/485 mode clear */
++#define TIOC_SBCS485  0x5460 /* TS72xx RTS/485 mode set */
++
+ /* Used for packet mode */
+ #define TIOCPKT_DATA           0
+ #define TIOCPKT_FLUSHREAD      1
index 13f767c..3a51a59 100644 (file)
@@ -73,6 +73,7 @@ SRC_URI_append_ts72xx = "\
        file://ep93xx-serial-clocks.diff;patch=1 \
        file://ep93xx-timer-accuracy.diff;patch=1 \
        file://ep93xx-maverick-uniqid.patch;patch=1 \
+       file://ep93xx-eth-phylib-framework.patch;patch=1 \
        file://ts72xx-nfbit-fix.patch;patch=1 \
        file://ts72xx-machine-id-fix.patch;patch=1 \
        file://ts72xx-watchdog.patch;patch=1 \