aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-2.6.24/ts72xx
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux-2.6.24/ts72xx')
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ep93xx-eth-phylib-framework.patch548
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ep93xx-gpio-interrupt-debounce.diff87
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ep93xx-i2c-bus.diff220
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ep93xx-i2c.diff110
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ep93xx-leds.diff181
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ep93xx-maverick-uniqid.patch38
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ep93xx-serial-clocks.diff42
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ep93xx-serial-uartbaud.diff66
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ep93xx-timer-accuracy.diff59
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/series13
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ts72xx-machine-id-fix.patch17
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ts72xx-nfbit-fix.patch15
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ts72xx-rs485.patch438
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ts72xx-use-cpld-reset.patch41
-rw-r--r--recipes/linux/linux-2.6.24/ts72xx/ts72xx-watchdog.patch430
15 files changed, 2305 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ep93xx-eth-phylib-framework.patch b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-eth-phylib-framework.patch
new file mode 100644
index 0000000000..3f25f308cd
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-eth-phylib-framework.patch
@@ -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;
+ }
+
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ep93xx-gpio-interrupt-debounce.diff b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-gpio-interrupt-debounce.diff
new file mode 100644
index 0000000000..27146c30f1
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-gpio-interrupt-debounce.diff
@@ -0,0 +1,87 @@
+
+Implement GPIO interrupt debouncing on ep93xx.
+
+Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org>
+
+Index: linux-2.6.22/arch/arm/mach-ep93xx/core.c
+===================================================================
+--- linux-2.6.22.orig/arch/arm/mach-ep93xx/core.c
++++ linux-2.6.22/arch/arm/mach-ep93xx/core.c
+@@ -154,6 +154,7 @@ struct sys_timer ep93xx_timer = {
+ *************************************************************************/
+ static unsigned char gpio_int_unmasked[3];
+ static unsigned char gpio_int_enabled[3];
++static unsigned char gpio_int_debounce[3];
+ static unsigned char gpio_int_type1[3];
+ static unsigned char gpio_int_type2[3];
+
+@@ -161,16 +162,19 @@ static void update_gpio_int_params(int a
+ {
+ if (abf == 0) {
+ __raw_writeb(0, EP93XX_GPIO_A_INT_ENABLE);
++ __raw_writeb(gpio_int_debounce[0], EP93XX_GPIO_A_INT_DEBOUNCE);
+ __raw_writeb(gpio_int_type2[0], EP93XX_GPIO_A_INT_TYPE2);
+ __raw_writeb(gpio_int_type1[0], EP93XX_GPIO_A_INT_TYPE1);
+ __raw_writeb(gpio_int_unmasked[0] & gpio_int_enabled[0], EP93XX_GPIO_A_INT_ENABLE);
+ } else if (abf == 1) {
+ __raw_writeb(0, EP93XX_GPIO_B_INT_ENABLE);
++ __raw_writeb(gpio_int_debounce[1], EP93XX_GPIO_B_INT_DEBOUNCE);
+ __raw_writeb(gpio_int_type2[1], EP93XX_GPIO_B_INT_TYPE2);
+ __raw_writeb(gpio_int_type1[1], EP93XX_GPIO_B_INT_TYPE1);
+ __raw_writeb(gpio_int_unmasked[1] & gpio_int_enabled[1], EP93XX_GPIO_B_INT_ENABLE);
+ } else if (abf == 2) {
+ __raw_writeb(0, EP93XX_GPIO_F_INT_ENABLE);
++ __raw_writeb(gpio_int_debounce[2], EP93XX_GPIO_F_INT_DEBOUNCE);
+ __raw_writeb(gpio_int_type2[2], EP93XX_GPIO_F_INT_TYPE2);
+ __raw_writeb(gpio_int_type1[2], EP93XX_GPIO_F_INT_TYPE1);
+ __raw_writeb(gpio_int_unmasked[2] & gpio_int_enabled[2], EP93XX_GPIO_F_INT_ENABLE);
+@@ -361,6 +365,13 @@ static int ep93xx_gpio_irq_type(unsigned
+ } else {
+ gpio_int_enabled[port] &= ~(1 << line);
+ }
++
++ if (type & IRQ_TYPE_DEBOUNCE) {
++ gpio_int_debounce[port] |= 1 << line;
++ } else {
++ gpio_int_debounce[port] &= ~(1 << line);
++ }
++
+ update_gpio_int_params(port);
+
+ return 0;
+Index: linux-2.6.22/include/asm-arm/arch-ep93xx/ep93xx-regs.h
+===================================================================
+--- linux-2.6.22.orig/include/asm-arm/arch-ep93xx/ep93xx-regs.h
++++ linux-2.6.22/include/asm-arm/arch-ep93xx/ep93xx-regs.h
+@@ -78,16 +78,19 @@
+ #define EP93XX_GPIO_F_INT_ACK EP93XX_GPIO_REG(0x54)
+ #define EP93XX_GPIO_F_INT_ENABLE EP93XX_GPIO_REG(0x58)
+ #define EP93XX_GPIO_F_INT_STATUS EP93XX_GPIO_REG(0x5c)
++#define EP93XX_GPIO_F_INT_DEBOUNCE EP93XX_GPIO_REG(0x64)
+ #define EP93XX_GPIO_A_INT_TYPE1 EP93XX_GPIO_REG(0x90)
+ #define EP93XX_GPIO_A_INT_TYPE2 EP93XX_GPIO_REG(0x94)
+ #define EP93XX_GPIO_A_INT_ACK EP93XX_GPIO_REG(0x98)
+ #define EP93XX_GPIO_A_INT_ENABLE EP93XX_GPIO_REG(0x9c)
+ #define EP93XX_GPIO_A_INT_STATUS EP93XX_GPIO_REG(0xa0)
++#define EP93XX_GPIO_A_INT_DEBOUNCE EP93XX_GPIO_REG(0xa8)
+ #define EP93XX_GPIO_B_INT_TYPE1 EP93XX_GPIO_REG(0xac)
+ #define EP93XX_GPIO_B_INT_TYPE2 EP93XX_GPIO_REG(0xb0)
+ #define EP93XX_GPIO_B_INT_ACK EP93XX_GPIO_REG(0xb4)
+ #define EP93XX_GPIO_B_INT_ENABLE EP93XX_GPIO_REG(0xb8)
+ #define EP93XX_GPIO_B_INT_STATUS EP93XX_GPIO_REG(0xbc)
++#define EP93XX_GPIO_B_INT_DEBOUNCE EP93XX_GPIO_REG(0xc4)
+
+ #define EP93XX_AAC_BASE (EP93XX_APB_VIRT_BASE + 0x00080000)
+
+Index: linux-2.6.22/include/linux/irq.h
+===================================================================
+--- linux-2.6.22.orig/include/linux/irq.h
++++ linux-2.6.22/include/linux/irq.h
+@@ -44,6 +44,7 @@ typedef void fastcall (*irq_flow_handler
+ #define IRQ_TYPE_LEVEL_LOW 0x00000008 /* Level low type */
+ #define IRQ_TYPE_SENSE_MASK 0x0000000f /* Mask of the above */
+ #define IRQ_TYPE_PROBE 0x00000010 /* Probing in progress */
++#define IRQ_TYPE_DEBOUNCE 0x00000020 /* Enable HW debounce */
+
+ /* Internal flags */
+ #define IRQ_INPROGRESS 0x00000100 /* IRQ handler active - do not enter! */
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ep93xx-i2c-bus.diff b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-i2c-bus.diff
new file mode 100644
index 0000000000..d3c66940de
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-i2c-bus.diff
@@ -0,0 +1,220 @@
+
+I2C bus driver using ep93xx GPIOs.
+
+Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org>
+
+Index: linux-2.6.22/drivers/i2c/busses/Kconfig
+===================================================================
+--- linux-2.6.22.orig/drivers/i2c/busses/Kconfig 2007-08-30 00:42:45.000000000 +0200
++++ linux-2.6.22/drivers/i2c/busses/Kconfig 2007-08-30 00:42:52.000000000 +0200
+@@ -635,4 +635,16 @@
+ This driver can also be built as a module. If so, the module
+ will be called i2c-pnx.
+
++config I2C_EP93XX
++ tristate "Cirrus Logic EP93XX GPIO-based I2C interface"
++ depends on I2C && ARCH_EP93XX
++ select I2C_ALGOBIT
++ help
++ Say Y here if you have an Cirrus Logic EP93XX based
++ system and are using GPIO lines for an I2C bus.
++
++ This support is also available as a module. If so, the module
++ will be called i2c-ep93xx.
++
++
+ endmenu
+Index: linux-2.6.22/drivers/i2c/busses/Makefile
+===================================================================
+--- linux-2.6.22.orig/drivers/i2c/busses/Makefile 2007-08-30 00:42:45.000000000 +0200
++++ linux-2.6.22/drivers/i2c/busses/Makefile 2007-08-30 00:42:52.000000000 +0200
+@@ -52,6 +52,7 @@
+ obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o
+ obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
+ obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
++obj-$(CONFIG_I2C_EP93XX) += i2c-ep93xx.o
+
+ ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
+ EXTRA_CFLAGS += -DDEBUG
+Index: linux-2.6.22/drivers/i2c/busses/i2c-ep93xx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/i2c/busses/i2c-ep93xx.c 2007-08-30 00:42:52.000000000 +0200
+@@ -0,0 +1,159 @@
++/*
++ * EP93XX I2C bus driver.
++ * Copyright (C) 2007 Lennert Buytenhek <buytenh@wantstofly.org>
++ *
++ * An I2C bus driver for the Cirrus Logic EP93xx SoC.
++ *
++ * Based on an earlier version by Alessandro Zummo.
++ */
++
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/module.h>
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++#include <asm/hardware.h>
++#include <asm/arch/gpio.h>
++
++struct ep93xx_i2c_priv {
++ struct ep93xx_i2c_data *data;
++ struct i2c_adapter adapter;
++ struct i2c_algo_bit_data algo_data;
++ int sda;
++ int scl;
++};
++
++
++static void ep93xx_bit_setsda(void *cookie, int val)
++{
++ struct ep93xx_i2c_priv *priv = cookie;
++
++ if (val) {
++ gpio_line_config(priv->data->sda_pin, GPIO_IN);
++ if (priv->scl && !priv->sda && priv->data->stop != NULL)
++ priv->data->stop(priv->data->cookie);
++ priv->sda = 1;
++ } else {
++ if (priv->scl && priv->sda && priv->data->start != NULL)
++ priv->data->start(priv->data->cookie);
++ gpio_line_config(priv->data->sda_pin, GPIO_OUT);
++ gpio_line_set(priv->data->sda_pin, 0);
++ priv->sda = 0;
++ }
++}
++
++static void ep93xx_bit_setscl(void *cookie, int val)
++{
++ struct ep93xx_i2c_priv *priv = cookie;
++
++ if (val) {
++ gpio_line_config(priv->data->scl_pin, GPIO_IN);
++ priv->scl = 1;
++ } else {
++ gpio_line_config(priv->data->scl_pin, GPIO_OUT);
++ gpio_line_set(priv->data->scl_pin, 0);
++ priv->scl = 0;
++ }
++}
++
++static int ep93xx_bit_getsda(void *cookie)
++{
++ struct ep93xx_i2c_priv *priv = cookie;
++
++ if (priv->sda == 0)
++ BUG();
++
++ return gpio_line_get(priv->data->sda_pin);
++}
++
++static int ep93xx_bit_getscl(void *cookie)
++{
++ struct ep93xx_i2c_priv *priv = cookie;
++
++ if (priv->scl == 0)
++ BUG();
++
++ return gpio_line_get(priv->data->scl_pin);
++}
++
++
++static int ep93xx_i2c_probe(struct platform_device *pdev)
++{
++ struct ep93xx_i2c_priv *priv;
++ int err;
++
++ priv = kzalloc(sizeof(struct ep93xx_i2c_priv), GFP_KERNEL);
++ if (priv == NULL)
++ return -ENOMEM;
++
++ priv->data = pdev->dev.platform_data;
++
++ strlcpy(priv->adapter.name, pdev->dev.driver->name, I2C_NAME_SIZE);
++ priv->adapter.algo_data = &priv->algo_data;
++ priv->adapter.class = I2C_CLASS_ALL;
++ priv->adapter.dev.parent = &pdev->dev;
++
++ priv->algo_data.data = priv;
++ priv->algo_data.setsda = ep93xx_bit_setsda;
++ priv->algo_data.setscl = ep93xx_bit_setscl;
++ priv->algo_data.getsda = ep93xx_bit_getsda;
++ priv->algo_data.getscl = ep93xx_bit_getscl;
++ priv->algo_data.udelay = 10;
++ priv->algo_data.timeout = 100;
++
++ priv->sda = 1;
++ gpio_line_config(priv->data->sda_pin, GPIO_IN);
++
++ priv->scl = 1;
++ gpio_line_config(priv->data->scl_pin, GPIO_IN);
++
++ err = i2c_bit_add_bus(&priv->adapter);
++ if (err) {
++ printk(KERN_ERR "ERROR: Could not install %s\n",
++ pdev->dev.bus_id);
++ kfree(priv);
++ return err;
++ }
++
++ platform_set_drvdata(pdev, priv);
++
++ return 0;
++}
++
++static int ep93xx_i2c_remove(struct platform_device *pdev)
++{
++ struct ep93xx_i2c_priv *priv;
++
++ priv = platform_get_drvdata(pdev);
++ i2c_del_adapter(&priv->adapter);
++ platform_set_drvdata(pdev, NULL);
++ kfree(priv);
++
++ return 0;
++}
++
++static struct platform_driver ep93xx_i2c_driver = {
++ .probe = ep93xx_i2c_probe,
++ .remove = ep93xx_i2c_remove,
++ .driver = {
++ .name = "ep93xx-i2c",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init ep93xx_i2c_init(void)
++{
++ return platform_driver_register(&ep93xx_i2c_driver);
++}
++
++static void __exit ep93xx_i2c_exit(void)
++{
++ platform_driver_unregister(&ep93xx_i2c_driver);
++}
++
++module_init(ep93xx_i2c_init);
++module_exit(ep93xx_i2c_exit);
++
++MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
++MODULE_DESCRIPTION("GPIO-based I2C adapter for EP93XX systems");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22/include/asm-arm/arch-ep93xx/platform.h
+===================================================================
+--- linux-2.6.22.orig/include/asm-arm/arch-ep93xx/platform.h 2007-08-30 00:42:45.000000000 +0200
++++ linux-2.6.22/include/asm-arm/arch-ep93xx/platform.h 2007-08-30 00:42:52.000000000 +0200
+@@ -16,5 +16,13 @@
+ unsigned char phy_id;
+ };
+
++struct ep93xx_i2c_data {
++ int sda_pin;
++ int scl_pin;
++ void *cookie;
++ void (*start)(void *);
++ void (*stop)(void *);
++};
++
+
+ #endif
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ep93xx-i2c.diff b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-i2c.diff
new file mode 100644
index 0000000000..b68fb14e2e
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-i2c.diff
@@ -0,0 +1,110 @@
+
+Instantiate the ep93xx gpio i2c bus driver in the generic ep93xx
+code.
+
+Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org>
+
+Index: linux-2.6.22/arch/arm/mach-ep93xx/core.c
+===================================================================
+--- linux-2.6.22.orig/arch/arm/mach-ep93xx/core.c 2007-08-30 00:42:49.000000000 +0200
++++ linux-2.6.22/arch/arm/mach-ep93xx/core.c 2007-08-30 00:43:00.000000000 +0200
+@@ -509,6 +509,52 @@
+ };
+
+
++static DEFINE_MUTEX(eeclk_eedat_mutex);
++static int i2c_transaction_in_progress;
++
++static void ep93xx_i2c_start_condition(void *cookie)
++{
++ if (!i2c_transaction_in_progress) {
++ mutex_lock(&eeclk_eedat_mutex);
++ i2c_transaction_in_progress = 1;
++ }
++}
++
++static void ep93xx_i2c_stop_condition(void *cookie)
++{
++ if (i2c_transaction_in_progress) {
++ mutex_unlock(&eeclk_eedat_mutex);
++ i2c_transaction_in_progress = 0;
++ } else {
++ printk(KERN_WARNING "ep93xx: i2c stop without start??\n");
++ }
++}
++
++static struct ep93xx_i2c_data ep93xx_i2c_gpio_data = {
++ .sda_pin = EP93XX_GPIO_LINE_EEDAT,
++ .scl_pin = EP93XX_GPIO_LINE_EECLK,
++ .start = ep93xx_i2c_start_condition,
++ .stop = ep93xx_i2c_stop_condition,
++};
++
++static struct platform_device ep93xx_i2c_device = {
++ .name = "ep93xx-i2c",
++ .id = 0,
++ .dev.platform_data = &ep93xx_i2c_gpio_data,
++ .num_resources = 0,
++};
++
++void eeclk_eedat_claim(void)
++{
++ mutex_lock(&eeclk_eedat_mutex);
++}
++
++void eeclk_eedat_release(void)
++{
++ mutex_unlock(&eeclk_eedat_mutex);
++}
++
++
+ void __init ep93xx_init_devices(void)
+ {
+ unsigned int v;
+@@ -521,10 +567,20 @@
+ __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
+ __raw_writel(v, EP93XX_SYSCON_DEVICE_CONFIG);
+
++ /*
++ * When EECLK/EEDAT are in open drain mode (EEDRIVE=0b11),
++ * writing a 1 to their Data Register bits causes subsequent
++ * reads from the Data Direction Register to return 'input',
++ * which confuses gpio_line_config(). So, we use CMOS drive
++ * mode instead.
++ */
++ __raw_writel(0, EP93XX_GPIO_EEDRIVE);
++
+ amba_device_register(&uart1_device, &iomem_resource);
+ amba_device_register(&uart2_device, &iomem_resource);
+ amba_device_register(&uart3_device, &iomem_resource);
+
+ platform_device_register(&ep93xx_rtc_device);
+ platform_device_register(&ep93xx_ohci_device);
++ platform_device_register(&ep93xx_i2c_device);
+ }
+Index: linux-2.6.22/include/asm-arm/arch-ep93xx/ep93xx-regs.h
+===================================================================
+--- linux-2.6.22.orig/include/asm-arm/arch-ep93xx/ep93xx-regs.h 2007-08-30 00:42:49.000000000 +0200
++++ linux-2.6.22/include/asm-arm/arch-ep93xx/ep93xx-regs.h 2007-08-30 00:43:00.000000000 +0200
+@@ -91,6 +91,7 @@
+ #define EP93XX_GPIO_B_INT_ENABLE EP93XX_GPIO_REG(0xb8)
+ #define EP93XX_GPIO_B_INT_STATUS EP93XX_GPIO_REG(0xbc)
+ #define EP93XX_GPIO_B_INT_DEBOUNCE EP93XX_GPIO_REG(0xc4)
++#define EP93XX_GPIO_EEDRIVE EP93XX_GPIO_REG(0xc8)
+
+ #define EP93XX_AAC_BASE (EP93XX_APB_VIRT_BASE + 0x00080000)
+
+Index: linux-2.6.22/include/asm-arm/arch-ep93xx/platform.h
+===================================================================
+--- linux-2.6.22.orig/include/asm-arm/arch-ep93xx/platform.h 2007-08-30 00:42:52.000000000 +0200
++++ linux-2.6.22/include/asm-arm/arch-ep93xx/platform.h 2007-08-30 00:43:00.000000000 +0200
+@@ -10,6 +10,9 @@
+ void ep93xx_init_devices(void);
+ extern struct sys_timer ep93xx_timer;
+
++void eeclk_eedat_claim(void);
++void eeclk_eedat_release(void);
++
+ struct ep93xx_eth_data
+ {
+ unsigned char dev_addr[6];
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ep93xx-leds.diff b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-leds.diff
new file mode 100644
index 0000000000..9836145c24
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-leds.diff
@@ -0,0 +1,181 @@
+
+EP93xx leds driver
+
+Signed-off-by: Petr Stetiar <ynezz@true.cz>
+
+Index: linux-2.6.24/arch/arm/mach-ep93xx/core.c
+===================================================================
+--- linux-2.6.24.orig/arch/arm/mach-ep93xx/core.c 2008-02-03 13:06:34.000000000 +0100
++++ linux-2.6.24/arch/arm/mach-ep93xx/core.c 2008-02-03 13:38:46.000000000 +0100
+@@ -555,6 +555,12 @@
+ }
+
+
++static struct platform_device ep93xx_led_device = {
++ .name = "ep93xx-led",
++ .id = -1,
++};
++
++
+ void __init ep93xx_init_devices(void)
+ {
+ unsigned int v;
+@@ -583,4 +589,5 @@
+ platform_device_register(&ep93xx_rtc_device);
+ platform_device_register(&ep93xx_ohci_device);
+ platform_device_register(&ep93xx_i2c_device);
++ platform_device_register(&ep93xx_led_device);
+ }
+Index: linux-2.6.24/drivers/leds/Kconfig
+===================================================================
+--- linux-2.6.24.orig/drivers/leds/Kconfig 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/leds/Kconfig 2008-02-03 13:38:46.000000000 +0100
+@@ -114,6 +114,12 @@
+ help
+ This option enables support for the CM-X270 LEDs.
+
++config LEDS_EP93XX
++ tristate "LED Support for Cirrus Logic EP93xx"
++ depends on LEDS_CLASS && ARCH_EP93XX
++ help
++ This option enables support for the Cirrus Logic EP93xx based boards.
++
+ comment "LED Triggers"
+
+ config LEDS_TRIGGERS
+Index: linux-2.6.24/drivers/leds/Makefile
+===================================================================
+--- linux-2.6.24.orig/drivers/leds/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/leds/Makefile 2008-02-03 13:40:49.000000000 +0100
+@@ -19,6 +19,7 @@
+ obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
+ obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
+ obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o
++obj-$(CONFIG_LEDS_EP93XX) += leds-ep93xx.o
+
+ # LED Triggers
+ obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
+Index: linux-2.6.24/drivers/leds/leds-ep93xx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.24/drivers/leds/leds-ep93xx.c 2008-02-03 13:38:46.000000000 +0100
+@@ -0,0 +1,119 @@
++/*
++ * LEDs driver for Cirrus Logic EP93xx
++ *
++ * Author: Petr Stetiar <ynezz@true.cz>
++ *
++ * Based on leds-corgi.c by Richard Purdie
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/leds.h>
++#include <asm/mach-types.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/gpio.h>
++
++static void ep93xx_green_led_set(struct led_classdev *led_cdev, enum led_brightness value)
++{
++ if (value)
++ gpio_line_set(EP93XX_GPIO_LINE_GRLED, EP93XX_GPIO_HIGH);
++ else
++ gpio_line_set(EP93XX_GPIO_LINE_GRLED, EP93XX_GPIO_LOW);
++}
++
++static void ep93xx_red_led_set(struct led_classdev *led_cdev, enum led_brightness value)
++{
++ if (value)
++ gpio_line_set(EP93XX_GPIO_LINE_RDLED, EP93XX_GPIO_HIGH);
++ else
++ gpio_line_set(EP93XX_GPIO_LINE_RDLED, EP93XX_GPIO_LOW);
++}
++
++
++static struct led_classdev ep93xx_green_led = {
++ .name = "ep93xx:green",
++ .default_trigger = "none",
++ .brightness_set = ep93xx_green_led_set,
++};
++
++static struct led_classdev ep93xx_red_led = {
++ .name = "ep93xx:red",
++ .default_trigger = "heartbeat",
++ .brightness_set = ep93xx_red_led_set,
++};
++
++#ifdef CONFIG_PM
++static int ep93xx_led_suspend(struct platform_device *dev, pm_message_t state)
++{
++ led_classdev_suspend(&ep93xx_green_led);
++ led_classdev_suspend(&ep93xx_red_led);
++ return 0;
++}
++
++static int ep93xx_led_resume(struct platform_device *dev)
++{
++ led_classdev_resume(&ep93xx_red_led);
++ led_classdev_resume(&ep93xx_green_led);
++ return 0;
++}
++#endif
++
++static int ep93xx_led_probe(struct platform_device *pdev)
++{
++ int ret;
++
++ gpio_line_config(EP93XX_GPIO_LINE_GRLED, GPIO_OUT);
++ gpio_line_config(EP93XX_GPIO_LINE_RDLED, GPIO_OUT);
++
++ ret = led_classdev_register(&pdev->dev, &ep93xx_green_led);
++ if (ret < 0)
++ return ret;
++
++ ret = led_classdev_register(&pdev->dev, &ep93xx_red_led);
++ if (ret < 0)
++ led_classdev_unregister(&ep93xx_green_led);
++
++ return ret;
++}
++
++static int ep93xx_led_remove(struct platform_device *pdev)
++{
++ led_classdev_unregister(&ep93xx_green_led);
++ led_classdev_unregister(&ep93xx_red_led);
++ return 0;
++}
++
++static struct platform_driver ep93xx_led_driver = {
++ .probe = ep93xx_led_probe,
++ .remove = ep93xx_led_remove,
++#ifdef CONFIG_PM
++ .suspend = ep93xx_led_suspend,
++ .resume = ep93xx_led_resume,
++#endif
++ .driver = {
++ .name = "ep93xx-led",
++ },
++};
++
++static int __init ep93xx_led_init(void)
++{
++ return platform_driver_register(&ep93xx_led_driver);
++}
++
++static void __exit ep93xx_led_exit(void)
++{
++ platform_driver_unregister(&ep93xx_led_driver);
++}
++
++module_init(ep93xx_led_init);
++module_exit(ep93xx_led_exit);
++
++MODULE_AUTHOR("Petr Stetiar <ynezz@true.cz>");
++MODULE_DESCRIPTION("Cirrus Logic EP93xx LED driver");
++MODULE_LICENSE("GPL");
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ep93xx-maverick-uniqid.patch b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-maverick-uniqid.patch
new file mode 100644
index 0000000000..fb6c8cfe18
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-maverick-uniqid.patch
@@ -0,0 +1,38 @@
+
+Adds support for SoC's unique ID (Maverick Key) in /proc/cpuinfo
+
+Signed-off-by: Petr Stetiar <ynezz@true.cz>
+
+Index: linux-2.6.22/arch/arm/kernel/setup.c
+===================================================================
+--- linux-2.6.22.orig/arch/arm/kernel/setup.c 2007-09-02 23:08:51.000000000 +0200
++++ linux-2.6.22/arch/arm/kernel/setup.c 2007-09-02 23:10:24.000000000 +0200
+@@ -959,8 +959,15 @@
+
+ seq_printf(m, "Hardware\t: %s\n", machine_name);
+ seq_printf(m, "Revision\t: %04x\n", system_rev);
++
++#if defined(CONFIG_ARCH_EP93XX)
++#include <asm/arch/ep93xx-regs.h>
++ seq_printf(m, "Serial\t\t: %016x\n",
++ *((unsigned int *)EP93XX_SECURITY_UNIQID));
++#else
+ seq_printf(m, "Serial\t\t: %08x%08x\n",
+ system_serial_high, system_serial_low);
++#endif
+
+ return 0;
+ }
+Index: linux-2.6.22/include/asm-arm/arch-ep93xx/ep93xx-regs.h
+===================================================================
+--- linux-2.6.22.orig/include/asm-arm/arch-ep93xx/ep93xx-regs.h 2007-09-02 23:06:45.000000000 +0200
++++ linux-2.6.22/include/asm-arm/arch-ep93xx/ep93xx-regs.h 2007-09-02 23:08:34.000000000 +0200
+@@ -70,6 +70,8 @@
+ #define EP93XX_I2S_BASE (EP93XX_APB_VIRT_BASE + 0x00020000)
+
+ #define EP93XX_SECURITY_BASE (EP93XX_APB_VIRT_BASE + 0x00030000)
++#define EP93XX_SECURITY_REG(x) (EP93XX_SECURITY_BASE + (x))
++#define EP93XX_SECURITY_UNIQID EP93XX_SECURITY_REG(0x2440)
+
+ #define EP93XX_GPIO_BASE (EP93XX_APB_VIRT_BASE + 0x00040000)
+ #define EP93XX_GPIO_REG(x) (EP93XX_GPIO_BASE + (x))
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ep93xx-serial-clocks.diff b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-serial-clocks.diff
new file mode 100644
index 0000000000..9eb2d9de98
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-serial-clocks.diff
@@ -0,0 +1,42 @@
+
+Hackishly enable all UART clocks before uncompressing the kernel,
+so that using ttyAM1 or ttyAM2 as console can work.
+
+Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org>
+
+Index: linux-2.6.22/include/asm-arm/arch-ep93xx/uncompress.h
+===================================================================
+--- linux-2.6.22.orig/include/asm-arm/arch-ep93xx/uncompress.h
++++ linux-2.6.22/include/asm-arm/arch-ep93xx/uncompress.h
+@@ -78,6 +78,23 @@ static void ethernet_reset(void)
+
+
+ /*
++ * We don't have clock management for the UARTs (amba-pl010)
++ * yet, so hackily enable all UART clocks here for now.
++ */
++#define PHYS_SYSCON_DEVICE_CONFIG 0x80930080
++#define PHYS_SYSCON_SWLOCK 0x809300c0
++
++static void enable_all_uart_clocks(void)
++{
++ unsigned int v;
++
++ v = __raw_readl(PHYS_SYSCON_DEVICE_CONFIG);
++ __raw_writel(0xaa, PHYS_SYSCON_SWLOCK);
++ __raw_writel(v | 0x01140000, PHYS_SYSCON_DEVICE_CONFIG);
++}
++
++
++/*
+ * Some bootloaders don't turn on the UARTBAUD bit, which means that
+ * the UARTs will be running off a divided 7.3728 MHz clock instead of
+ * the 14.7456 MHz peripheral clock when linux boots.
+@@ -126,6 +143,7 @@ static void fix_uart_base(void)
+ static void arch_decomp_setup(void)
+ {
+ ethernet_reset();
++ enable_all_uart_clocks();
+ fix_uart_base();
+ }
+
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ep93xx-serial-uartbaud.diff b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-serial-uartbaud.diff
new file mode 100644
index 0000000000..7183ab626e
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-serial-uartbaud.diff
@@ -0,0 +1,66 @@
+
+Force UARTBAUD on before uncompressing.
+
+Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org>
+
+Index: linux-2.6.22/include/asm-arm/arch-ep93xx/uncompress.h
+===================================================================
+--- linux-2.6.22.orig/include/asm-arm/arch-ep93xx/uncompress.h
++++ linux-2.6.22/include/asm-arm/arch-ep93xx/uncompress.h
+@@ -77,9 +77,56 @@ static void ethernet_reset(void)
+ }
+
+
++/*
++ * Some bootloaders don't turn on the UARTBAUD bit, which means that
++ * the UARTs will be running off a divided 7.3728 MHz clock instead of
++ * the 14.7456 MHz peripheral clock when linux boots.
++ *
++ * We detect that condition here and fix it by turning on UARTBAUD, and
++ * then reprogramming the divisors on all enabled UARTs to twice what
++ * they were before we turned UARTBAUD on, to preserve the programmed
++ * baud rate.
++ */
++#define PHYS_SYSCON_CLOCK_CONTROL 0x80930004
++#define SYSCON_CLOCK_UARTBAUD 0x20000000
++#define PHYS_UART1_BASE 0x808c0000
++#define PHYS_UART2_BASE 0x808d0000
++#define PHYS_UART3_BASE 0x808e0000
++
++static void uart_divisor_times_two(unsigned int base)
++{
++ u16 divisor;
++
++ divisor = __raw_readb(base + 0x0c) << 8;
++ divisor |= __raw_readb(base + 0x10);
++ if (divisor) {
++ divisor = (2 * (divisor + 1)) - 1;
++ __raw_writeb(divisor >> 8, base + 0x0c);
++ __raw_writeb(divisor & 0xff, base + 0x10);
++ __raw_writeb(__raw_readb(base + 0x08), base + 0x08);
++ }
++}
++
++static void fix_uart_base(void)
++{
++ unsigned int v;
++
++ v = __raw_readl(PHYS_SYSCON_CLOCK_CONTROL);
++ if ((v & SYSCON_CLOCK_UARTBAUD) == 0) {
++ v |= SYSCON_CLOCK_UARTBAUD;
++ __raw_writel(v, PHYS_SYSCON_CLOCK_CONTROL);
++
++ uart_divisor_times_two(PHYS_UART1_BASE);
++ uart_divisor_times_two(PHYS_UART2_BASE);
++ uart_divisor_times_two(PHYS_UART3_BASE);
++ }
++}
++
++
+ static void arch_decomp_setup(void)
+ {
+ ethernet_reset();
++ fix_uart_base();
+ }
+
+ #define arch_decomp_wdog()
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ep93xx-timer-accuracy.diff b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-timer-accuracy.diff
new file mode 100644
index 0000000000..8254153b69
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ep93xx-timer-accuracy.diff
@@ -0,0 +1,59 @@
+
+The ep93xx has a weird timer tick base (983.04 kHz.) This experimental
+patch tries to increase time of day accuracy by keeping the number of
+ticks until the next jiffy in a fractional value representation.
+
+Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org>
+
+Index: linux-2.6.22/arch/arm/mach-ep93xx/core.c
+===================================================================
+--- linux-2.6.22.orig/arch/arm/mach-ep93xx/core.c
++++ linux-2.6.22/arch/arm/mach-ep93xx/core.c
+@@ -94,19 +94,32 @@ void __init ep93xx_map_io(void)
+ * track of lost jiffies.
+ */
+ static unsigned int last_jiffy_time;
++static unsigned int next_jiffy_time;
++static unsigned int accumulator;
+
+-#define TIMER4_TICKS_PER_JIFFY ((CLOCK_TICK_RATE + (HZ/2)) / HZ)
++#define TIMER4_TICKS_PER_JIFFY (983040 / HZ)
++#define TIMER4_TICKS_MOD_JIFFY (983040 % HZ)
++
++static int after_eq(unsigned long a, unsigned long b)
++{
++ return ((signed long)(a - b)) >= 0;
++}
+
+ static int ep93xx_timer_interrupt(int irq, void *dev_id)
+ {
+ write_seqlock(&xtime_lock);
+
+ __raw_writel(1, EP93XX_TIMER1_CLEAR);
+- while ((signed long)
+- (__raw_readl(EP93XX_TIMER4_VALUE_LOW) - last_jiffy_time)
+- >= TIMER4_TICKS_PER_JIFFY) {
+- last_jiffy_time += TIMER4_TICKS_PER_JIFFY;
++ while (after_eq(__raw_readl(EP93XX_TIMER4_VALUE_LOW), next_jiffy_time)) {
+ timer_tick();
++
++ last_jiffy_time = next_jiffy_time;
++ next_jiffy_time += TIMER4_TICKS_PER_JIFFY;
++ accumulator += TIMER4_TICKS_MOD_JIFFY;
++ if (accumulator >= HZ) {
++ next_jiffy_time++;
++ accumulator -= HZ;
++ }
+ }
+
+ write_sequnlock(&xtime_lock);
+Index: linux-2.6.22/include/asm-arm/arch-ep93xx/timex.h
+===================================================================
+--- linux-2.6.22.orig/include/asm-arm/arch-ep93xx/timex.h
++++ linux-2.6.22/include/asm-arm/arch-ep93xx/timex.h
+@@ -2,4 +2,4 @@
+ * linux/include/asm-arm/arch-ep93xx/timex.h
+ */
+
+-#define CLOCK_TICK_RATE 983040
++#define CLOCK_TICK_RATE (1000 * HZ)
diff --git a/recipes/linux/linux-2.6.24/ts72xx/series b/recipes/linux/linux-2.6.24/ts72xx/series
new file mode 100644
index 0000000000..3295838606
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/series
@@ -0,0 +1,13 @@
+ep93xx-gpio-interrupt-debounce.diff
+ep93xx-i2c-bus.diff
+ep93xx-i2c.diff
+ep93xx-leds.diff
+ep93xx-serial-uartbaud.diff
+ep93xx-serial-clocks.diff
+ep93xx-timer-accuracy.diff
+ep93xx-maverick-uniqid.patch
+ts72xx-machine-id-fix.patch
+ts72xx-nfbit-fix.patch
+ts72xx-watchdog.patch
+ts72xx-use-cpld-reset.patch
+ts72xx-rs485.patch
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ts72xx-machine-id-fix.patch b/recipes/linux/linux-2.6.24/ts72xx/ts72xx-machine-id-fix.patch
new file mode 100644
index 0000000000..64c38398db
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ts72xx-machine-id-fix.patch
@@ -0,0 +1,17 @@
+
+Fix wrong machine ID passed from RedBoot
+
+Signed-off-by: Petr Stetiar <ynezz@true.cz>
+
+Index: linux-2.6.22/arch/arm/kernel/head.S
+===================================================================
+--- linux-2.6.22.orig/arch/arm/kernel/head.S 2007-08-30 00:42:45.000000000 +0200
++++ linux-2.6.22/arch/arm/kernel/head.S 2007-08-30 00:43:13.000000000 +0200
+@@ -82,6 +82,7 @@
+ bl __lookup_processor_type @ r5=procinfo r9=cpuid
+ movs r10, r5 @ invalid processor (r5=0)?
+ beq __error_p @ yes, error 'p'
++ ldr r1, =0x000002a1 @ mach-type = TS-7250
+ bl __lookup_machine_type @ r5=machinfo
+ movs r8, r5 @ invalid machine (r5=0)?
+ beq __error_a @ yes, error 'a'
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ts72xx-nfbit-fix.patch b/recipes/linux/linux-2.6.24/ts72xx/ts72xx-nfbit-fix.patch
new file mode 100644
index 0000000000..eab73154a8
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ts72xx-nfbit-fix.patch
@@ -0,0 +1,15 @@
+
+Force the nF bit on. Usually this is set by the bootrom. If it is not set,
+then the CPU core will run from HCLK instead of FCLK, and performance will
+suffer. If you see BogoMIPS of about 1/4 of your CPU clock, try turning this
+on; your performance should double.
+
+--- linux-2.6.21.4/arch/arm/mm/proc-arm920.S 2007-06-07 23:27:31.000000000 +0200
++++ linux-2.6.21.4-arm/arch/arm/mm/proc-arm920.S 2007-06-08 22:59:48.000000000 +0200
+@@ -395,6 +395,7 @@
+ mrc p15, 0, r0, c1, c0 @ get control register v4
+ bic r0, r0, r5
+ orr r0, r0, r6
++ orr r0, r0, #0x40000000
+ mov pc, lr
+ .size __arm920_setup, . - __arm920_setup
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ts72xx-rs485.patch b/recipes/linux/linux-2.6.24/ts72xx/ts72xx-rs485.patch
new file mode 100644
index 0000000000..17ee397f45
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ts72xx-rs485.patch
@@ -0,0 +1,438 @@
+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
+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
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ts72xx-use-cpld-reset.patch b/recipes/linux/linux-2.6.24/ts72xx/ts72xx-use-cpld-reset.patch
new file mode 100644
index 0000000000..9744a67653
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ts72xx-use-cpld-reset.patch
@@ -0,0 +1,41 @@
+
+Use CPLD watchdog to reset the machine instead of buggy ep93xx one, which
+sometimes get stuck...
+
+Signed-off-by: Petr Stetiar <ynezz@true.cz>
+
+Index: linux-2.6.22/include/asm-arm/arch-ep93xx/system.h
+===================================================================
+--- linux-2.6.22.orig/include/asm-arm/arch-ep93xx/system.h 2007-08-30 00:53:47.000000000 +0200
++++ linux-2.6.22/include/asm-arm/arch-ep93xx/system.h 2007-08-30 00:54:28.000000000 +0200
+@@ -3,6 +3,7 @@
+ */
+
+ #include <asm/hardware.h>
++#include <asm/mach-types.h>
+
+ static inline void arch_idle(void)
+ {
+@@ -15,11 +16,17 @@
+
+ local_irq_disable();
+
+- devicecfg = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG);
+- __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
+- __raw_writel(devicecfg | 0x80000000, EP93XX_SYSCON_DEVICE_CONFIG);
+- __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
+- __raw_writel(devicecfg & ~0x80000000, EP93XX_SYSCON_DEVICE_CONFIG);
++ if (machine_is_ts72xx()) {
++ __raw_writeb(0x5, TS72XX_WATCHDOG_FEED_PHYS_BASE);
++ __raw_writeb(0x1, TS72XX_WATCHDOG_CONTROL_PHYS_BASE);
++ } else {
++ devicecfg = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG);
++ __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
++ __raw_writel(devicecfg | 0x80000000, EP93XX_SYSCON_DEVICE_CONFIG);
++ __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
++ __raw_writel(devicecfg & ~0x80000000, EP93XX_SYSCON_DEVICE_CONFIG);
++ }
++
+
+ while (1)
+ ;
diff --git a/recipes/linux/linux-2.6.24/ts72xx/ts72xx-watchdog.patch b/recipes/linux/linux-2.6.24/ts72xx/ts72xx-watchdog.patch
new file mode 100644
index 0000000000..ff29c14b55
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/ts72xx/ts72xx-watchdog.patch
@@ -0,0 +1,430 @@
+
+TS-72xx watchdog driver
+
+Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
+
+Index: linux-2.6.24/arch/arm/mach-ep93xx/ts72xx.c
+===================================================================
+--- linux-2.6.24.orig/arch/arm/mach-ep93xx/ts72xx.c 2008-02-03 14:09:54.000000000 +0100
++++ linux-2.6.24/arch/arm/mach-ep93xx/ts72xx.c 2008-02-03 14:16:09.000000000 +0100
+@@ -183,6 +183,26 @@
+ .resource = ts72xx_eth_resource,
+ };
+
++static struct resource ts72xx_watchdog_resources[] = {
++ [0] = {
++ .start = TS72XX_WATCHDOG_CONTROL_PHYS_BASE,
++ .end = TS72XX_WATCHDOG_CONTROL_PHYS_BASE + 0x0fff,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = TS72XX_WATCHDOG_FEED_PHYS_BASE,
++ .end = TS72XX_WATCHDOG_FEED_PHYS_BASE + 0x0fff,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static struct platform_device ts72xx_watchdog_device = {
++ .name = "ts72xx_wdt",
++ .id = -1,
++ .num_resources = ARRAY_SIZE(ts72xx_watchdog_resources),
++ .resource = ts72xx_watchdog_resources,
++};
++
+ static void __init ts72xx_init_machine(void)
+ {
+ ep93xx_init_devices();
+@@ -193,6 +213,7 @@
+ memcpy(ts72xx_eth_data.dev_addr,
+ (void *)(EP93XX_ETHERNET_BASE + 0x50), 6);
+ platform_device_register(&ts72xx_eth_device);
++ platform_device_register(&ts72xx_watchdog_device);
+ }
+
+ MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC")
+Index: linux-2.6.24/drivers/watchdog/Kconfig
+===================================================================
+--- linux-2.6.24.orig/drivers/watchdog/Kconfig 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/watchdog/Kconfig 2008-02-03 14:16:09.000000000 +0100
+@@ -247,6 +247,18 @@
+
+ # H8300 Architecture
+
++config TS72XX_WATCHDOG
++ tristate "TS-72xx Watchdog"
++ depends on WATCHDOG && ARCH_EP93XX && MACH_TS72XX
++ help
++ Say Y here if to include support for the CPLD watchdog
++ included on Technologic Systems SBC.
++
++ NOTE: timeout value is given in milliseconds, not in seconds.
++
++ To compile this driver as a module, choose M here: the
++ module will be called ts72xx_wdt.
++
+ # X86 (i386 + ia64 + x86_64) Architecture
+
+ config ACQUIRE_WDT
+Index: linux-2.6.24/drivers/watchdog/Makefile
+===================================================================
+--- linux-2.6.24.orig/drivers/watchdog/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/watchdog/Makefile 2008-02-03 14:16:09.000000000 +0100
+@@ -36,6 +36,7 @@
+ obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
+ obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
+ obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
++obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
+ obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
+ obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
+ obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
+Index: linux-2.6.24/drivers/watchdog/ts72xx_wdt.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.24/drivers/watchdog/ts72xx_wdt.c 2008-02-03 14:16:09.000000000 +0100
+@@ -0,0 +1,332 @@
++/*
++ * TS-72xx Watchdog Driver for Technologic Systems boards.
++ *
++ * Based on ep93xx_wdt.c by Lehtiniemi <rayl@mail.com> &
++ * Alessandro Zummo <a.zummo@towertech.it>
++ * and ib700wdt.c by Charles Howes <chowes@vsol.net>
++ * and mpc83xx_wdt.c by Dave Updegraff <dave@cray.org> &
++ * Kumar Gala <galak@kernel.crashing.org>
++ *
++ * (c) Copyright 2006 Matthieu Crapet <mcrapet@gmail.com>
++ *
++ * 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 the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ *
++ * This driver only deals with native timeout provided by CPLD :
++ * 1/4s, 1/2s, 1s, 2s, 4s and 8s. No external timer is used.
++ * Notice that we must ping before modifying the control register.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/platform_device.h>
++#include <linux/init.h>
++#include <linux/watchdog.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <asm/mach-types.h>
++
++#define WATCHDOG_VERSION "0.2"
++#define PFX "ts72xx_wdt: "
++
++#define WATCHDOG_TIMEOUT 8000 /* 8 seconds */
++#define WDT_IN_USE 0
++#define WDT_OK_TO_CLOSE 1
++
++static unsigned long ts72xx_wdt_status;
++static unsigned char ts72xx_wdt_cpld_value = 0x7;
++static int nowayout = WATCHDOG_NOWAYOUT;
++static int timeout = WATCHDOG_TIMEOUT;
++
++static int ts72xx_wdt_times[12] = {
++ 6000, 3000, 1500, 750, 275, 0,
++ 8000, 4000, 2000, 1000, 500, 250
++};
++
++static void __iomem *control_register;
++static void __iomem *feed_register;
++
++
++/*
++ * Kernel methods.
++ */
++
++static inline void ts72xx_wdt_ping(void)
++{
++ __raw_writew(0x05, feed_register);
++}
++
++static inline void ts72xx_wdt_enable(void)
++{
++ __raw_writew(0x05, feed_register);
++ __raw_writew(ts72xx_wdt_cpld_value, control_register);
++}
++
++static inline void ts72xx_wdt_disable(void)
++{
++ __raw_writew(0x05, feed_register);
++ __raw_writew(0, control_register);
++}
++
++static inline void ts72xx_parse_timeout(int value)
++{
++ unsigned char cpld_value = 0x7;
++ int i;
++
++ if ((value > 8000) || (value < 250)) {
++ timeout = WATCHDOG_TIMEOUT;
++ printk(KERN_INFO PFX "Timeout value out of range, set to %d\n", timeout);
++ } else {
++ for (i = 0; i < 6; i++) {
++ if (value >= ts72xx_wdt_times[i]) {
++ timeout = ts72xx_wdt_times[i+6];
++
++ if (value != timeout)
++ printk(KERN_INFO PFX "Timeout value rounded to %d\n", timeout);
++
++ if (i >= 3) /* cpld_value can't be 4 */
++ i++;
++
++ cpld_value = 7 - i;
++ break;
++ }
++ }
++ }
++
++ ts72xx_wdt_cpld_value = cpld_value;
++}
++
++static ssize_t ts72xx_wdt_write(struct file *file, const char __user *buf,
++ size_t count, loff_t *ppos)
++{
++ /* Can't seek (pwrite) on this device */
++ if (*ppos != file->f_pos)
++ return -ESPIPE;
++
++ if (count) {
++ if (!nowayout) {
++ size_t i;
++
++ clear_bit(WDT_OK_TO_CLOSE, &ts72xx_wdt_status);
++
++ for (i = 0; i != count; i++) {
++ char c;
++
++ if (get_user(c, buf + i))
++ return -EFAULT;
++
++ if (c == 'V')
++ set_bit(WDT_OK_TO_CLOSE, &ts72xx_wdt_status);
++ else
++ clear_bit(WDT_OK_TO_CLOSE, &ts72xx_wdt_status);
++ }
++ }
++ ts72xx_wdt_ping();
++ }
++
++ return count;
++}
++
++static int ts72xx_wdt_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ int new_margin;
++ int ret = -ENOIOCTLCMD;
++
++ static struct watchdog_info ident = {
++ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
++ .firmware_version = 1,
++ .identity = "TS-72xx Watchdog",
++ };
++
++ switch (cmd) {
++ case WDIOC_GETSUPPORT:
++ ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
++ sizeof(ident)) ? -EFAULT : 0;
++ break;
++
++ case WDIOC_GETSTATUS:
++ case WDIOC_GETBOOTSTATUS:
++ ret = put_user(0, (int __user *)arg);
++ break;
++
++ case WDIOC_KEEPALIVE:
++ ts72xx_wdt_ping();
++ ret = 0;
++ break;
++
++ case WDIOC_SETTIMEOUT:
++ if (get_user(new_margin, (int __user *)arg))
++ return -EFAULT;
++
++ ts72xx_parse_timeout(new_margin);
++ ts72xx_wdt_enable();
++ /* Fall */
++
++ case WDIOC_GETTIMEOUT:
++ ret = put_user(timeout, (int __user *)arg);
++ break;
++ }
++
++ return ret;
++}
++
++static int ts72xx_wdt_open(struct inode *inode, struct file *file)
++{
++ if (test_and_set_bit(WDT_IN_USE, &ts72xx_wdt_status))
++ return -EBUSY;
++
++ if (nowayout) {
++ __module_get(THIS_MODULE);
++ }
++
++ ts72xx_wdt_enable();
++ ts72xx_wdt_ping();
++
++ return nonseekable_open(inode, file);
++}
++
++static int ts72xx_wdt_close(struct inode *inode, struct file *file)
++{
++ if (test_bit(WDT_OK_TO_CLOSE, &ts72xx_wdt_status))
++ ts72xx_wdt_disable();
++ else
++ printk(KERN_CRIT PFX "Device file closed unexpectedly. "
++ "Will not stop the WDT!\n");
++
++ clear_bit(WDT_IN_USE, &ts72xx_wdt_status);
++
++ return 0;
++}
++
++/*
++ * Kernel Interfaces
++ */
++
++static struct file_operations ts72xx_wdt_fops = {
++ .owner = THIS_MODULE,
++ .llseek = no_llseek,
++ .write = ts72xx_wdt_write,
++ .ioctl = ts72xx_wdt_ioctl,
++ .open = ts72xx_wdt_open,
++ .release = ts72xx_wdt_close,
++};
++
++static struct miscdevice ts72xx_wdt_miscdev = {
++ .minor = WATCHDOG_MINOR,
++ .name = "watchdog",
++ .fops = &ts72xx_wdt_fops,
++};
++
++static void ts72xx_wdt_shutdown(struct platform_device *dev)
++{
++ ts72xx_wdt_disable();
++}
++
++static int __devinit ts72xx_wdt_probe(struct platform_device *dev)
++{
++ struct resource *r;
++ int ret;
++
++ if (!machine_is_ts72xx())
++ return -ENODEV;
++
++ r = platform_get_resource(dev, IORESOURCE_MEM, 0);
++
++ if (!r) {
++ ret = -ENODEV;
++ goto err_out;
++ }
++
++ control_register = ioremap(r->start, r->end - r->start + 1);
++
++ if (control_register == NULL) {
++ ret = -ENOMEM;
++ goto err_out;
++ }
++
++ r = platform_get_resource(dev, IORESOURCE_MEM, 1);
++
++ if (!r) {
++ ret = -ENODEV;
++ goto err_unmap1;
++ }
++
++ feed_register = ioremap(r->start, r->end - r->start + 1);
++
++ if (feed_register == NULL) {
++ ret = -ENOMEM;
++ goto err_unmap1;
++ }
++
++ ret = misc_register(&ts72xx_wdt_miscdev);
++ if (ret) {
++ printk(KERN_ERR PFX "cannot register miscdev on minor=%d "
++ "(err=%d), ep93xx_watchdog already loaded?!\n", WATCHDOG_MINOR, ret);
++ goto err_unmap2;
++ }
++
++ printk(KERN_INFO PFX "TS-72xx watchdog driver, v%s\n", WATCHDOG_VERSION);
++ ts72xx_parse_timeout(timeout);
++
++ return 0;
++
++err_unmap2:
++ iounmap(feed_register);
++err_unmap1:
++ iounmap(control_register);
++err_out:
++ return ret;
++}
++
++static int __devexit ts72xx_wdt_remove(struct platform_device *dev)
++{
++ misc_deregister(&ts72xx_wdt_miscdev);
++ iounmap(feed_register);
++ iounmap(control_register);
++
++ return 0;
++}
++
++static struct platform_driver ts72xx_wdt_driver = {
++ .probe = ts72xx_wdt_probe,
++ .remove = __devexit_p(ts72xx_wdt_remove),
++ .shutdown = ts72xx_wdt_shutdown,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "ts72xx_wdt",
++ },
++};
++
++static int __init ts72xx_wdt_init(void)
++{
++ return platform_driver_register(&ts72xx_wdt_driver);
++}
++
++static void __exit ts72xx_wdt_exit(void)
++{
++ platform_driver_unregister(&ts72xx_wdt_driver);
++}
++
++module_init(ts72xx_wdt_init);
++module_exit(ts72xx_wdt_exit);
++
++#ifdef CONFIG_WATCHDOG_NOWAYOUT
++module_param(nowayout, int, 0);
++MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
++#endif
++
++module_param(timeout, int, 0);
++MODULE_PARM_DESC(timeout,"Watchdog timeout in milliseconds (250..8000, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
++
++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>");
++MODULE_DESCRIPTION("TS-72xx watchdog driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+Index: linux-2.6.24/include/asm-arm/arch-ep93xx/ts72xx.h
+===================================================================
+--- linux-2.6.24.orig/include/asm-arm/arch-ep93xx/ts72xx.h 2008-02-03 14:09:54.000000000 +0100
++++ linux-2.6.24/include/asm-arm/arch-ep93xx/ts72xx.h 2008-02-03 14:16:09.000000000 +0100
+@@ -69,6 +69,9 @@
+ #define TS72XX_RTC_DATA_SIZE 0x00001000
+
+
++#define TS72XX_WATCHDOG_CONTROL_PHYS_BASE 0x23800000
++#define TS72XX_WATCHDOG_FEED_PHYS_BASE 0x23c00000
++
+ #ifndef __ASSEMBLY__
+ #include <asm/io.h>
+