Index: linux-2.6.18-rc1-git9/drivers/net/arm/Kconfig =================================================================== --- linux-2.6.18-rc1-git9.orig/drivers/net/arm/Kconfig +++ linux-2.6.18-rc1-git9/drivers/net/arm/Kconfig @@ -39,3 +39,10 @@ config ARM_AT91_ETHER help If you wish to compile a kernel for the AT91RM9200 and enable ethernet support, then you should always answer Y to this. + +config EP93XX_ETH + tristate "EP93xx Ethernet support" + depends on NET_ETHERNET && ARM && ARCH_EP93XX + 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. Index: linux-2.6.18-rc1-git9/drivers/net/arm/Makefile =================================================================== --- linux-2.6.18-rc1-git9.orig/drivers/net/arm/Makefile +++ linux-2.6.18-rc1-git9/drivers/net/arm/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_ARM_ETHERH) += etherh.o obj-$(CONFIG_ARM_ETHER3) += ether3.o obj-$(CONFIG_ARM_ETHER1) += ether1.o obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o +obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o Index: linux-2.6.18-rc1-git9/drivers/net/arm/ep93xx_eth.c =================================================================== --- /dev/null +++ linux-2.6.18-rc1-git9/drivers/net/arm/ep93xx_eth.c @@ -0,0 +1,802 @@ +/* + * EP93xx ethernet network device driver + * Copyright (C) 2006 Lennert Buytenhek + * Dedicated to Marija Kulikova. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ep93xx_eth.h" + +#define DRV_MODULE_NAME "ep93xx-eth" +#define DRV_MODULE_VERSION "0.1" + +#define RX_QUEUE_ENTRIES 64 +#define TX_QUEUE_ENTRIES 8 + +#define MAX_PKT_SIZE 2044 +#define PKT_BUF_SIZE 2048 + +struct ep93xx_descs +{ + struct ep93xx_rdesc rdesc[RX_QUEUE_ENTRIES]; + struct ep93xx_tdesc tdesc[TX_QUEUE_ENTRIES]; + struct ep93xx_rstat rstat[RX_QUEUE_ENTRIES]; + struct ep93xx_tstat tstat[TX_QUEUE_ENTRIES]; +}; + +struct ep93xx_priv +{ + struct resource *res; + void *base_addr; + int irq; + + struct ep93xx_descs *descs; + dma_addr_t descs_dma_addr; + + void *rx_buf[RX_QUEUE_ENTRIES]; + void *tx_buf[TX_QUEUE_ENTRIES]; + + spinlock_t rx_lock; + int rx_pointer; + int tx_clean_pointer; + int tx_pointer; + spinlock_t tx_pending_lock; + int tx_pending; + + struct net_device_stats stats; + + struct mii_if_info mii; + u8 mdc_divisor; + u8 mdc_preamble_suppress; +}; + +#define rdb(ep, off) __raw_readb((ep)->base_addr + (off)) +#define rdw(ep, off) __raw_readw((ep)->base_addr + (off)) +#define rdl(ep, off) __raw_readl((ep)->base_addr + (off)) +#define wrb(ep, off, val) __raw_writeb((val), (ep)->base_addr + (off)) +#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 struct net_device_stats *ep93xx_get_stats(struct net_device *dev) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + return &(ep->stats); +} + +static int ep93xx_rx(struct net_device *dev, int *budget) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + int tail_offset; + int rx_done; + int processed; + + tail_offset = rdl(ep, 0x00a8) - ep->descs_dma_addr; + + rx_done = 0; + processed = 0; + while (*budget > 0) { + int entry; + struct ep93xx_rstat *rstat; + u32 rstat0; + u32 rstat1; + int length; + struct sk_buff *skb; + + entry = ep->rx_pointer; + rstat = ep->descs->rstat + entry; + if ((void *)rstat - (void *)ep->descs == tail_offset) { + rx_done = 1; + break; + } + + rstat0 = rstat->rstat0; + rstat1 = rstat->rstat1; + rstat->rstat0 = 0; + rstat->rstat1 = 0; + + if (!(rstat0 & RSTAT0_RFP)) { + printk(KERN_CRIT "ep93xx_rx: buffer not done " + " %.8x %.8x\n", rstat0, rstat1); + BUG(); + } + if (!(rstat0 & RSTAT0_EOF)) { + printk(KERN_CRIT "ep93xx_rx: not end-of-frame " + " %.8x %.8x\n", rstat0, rstat1); + BUG(); + } + if (!(rstat0 & RSTAT0_EOB)) { + printk(KERN_CRIT "ep93xx_rx: not end-of-buffer " + " %.8x %.8x\n", rstat0, rstat1); + BUG(); + } + if (!(rstat1 & RSTAT1_RFP)) { + printk(KERN_CRIT "ep93xx_rx: buffer1 not done " + " %.8x %.8x\n", rstat0, rstat1); + BUG(); + } + if ((rstat1 & RSTAT1_BUFFER_INDEX) >> 16 != entry) { + printk(KERN_CRIT "ep93xx_rx: entry mismatch " + " %.8x %.8x\n", rstat0, rstat1); + BUG(); + } + + if (!(rstat0 & RSTAT0_RWE)) { + printk(KERN_NOTICE "ep93xx_rx: receive error " + " %.8x %.8x\n", rstat0, rstat1); + + ep->stats.rx_errors++; + if (rstat0 & RSTAT0_OE) + ep->stats.rx_fifo_errors++; + if (rstat0 & RSTAT0_FE) + ep->stats.rx_frame_errors++; + if (rstat0 & (RSTAT0_RUNT | RSTAT0_EDATA)) + ep->stats.rx_length_errors++; + if (rstat0 & RSTAT0_CRCE) + ep->stats.rx_crc_errors++; + goto err; + } + + length = rstat1 & RSTAT1_FRAME_LENGTH; + if (length < 4 || length > MAX_PKT_SIZE) { + printk(KERN_NOTICE "ep93xx_rx: invalid length " + " %.8x %.8x\n", rstat0, rstat1); + goto err; + } + + /* Strip FCS. */ + if (rstat0 & RSTAT0_CRCI) + length -= 4; + + skb = dev_alloc_skb(length + 2); + if (likely(skb != NULL)) { + skb->dev = dev; + skb_reserve(skb, 2); + dma_sync_single(NULL, ep->descs->rdesc[entry].buf_addr, + length, DMA_FROM_DEVICE); + eth_copy_and_sum(skb, ep->rx_buf[entry], length, 0); + skb_put(skb, length); + skb->protocol = eth_type_trans(skb, dev); + + dev->last_rx = jiffies; + + netif_receive_skb(skb); + + ep->stats.rx_packets++; + ep->stats.rx_bytes += length; + } else { + ep->stats.rx_dropped++; + } + +err: + ep->rx_pointer = (entry + 1) % RX_QUEUE_ENTRIES; + processed++; + dev->quota--; + (*budget)--; + } + + if (processed) { + wrw(ep, 0x009c, processed); + wrw(ep, 0x00ac, processed); + } + + return !rx_done; +} + +static int ep93xx_have_more_rx(struct ep93xx_priv *ep) +{ + struct ep93xx_rstat *rstat; + int tail_offset; + + rstat = ep->descs->rstat + ep->rx_pointer; + tail_offset = rdl(ep, 0x00a8) - ep->descs_dma_addr; + + return !((void *)rstat - (void *)ep->descs == tail_offset); +} + +static int ep93xx_poll(struct net_device *dev, int *budget) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + + /* @@@ Have to stop polling when device is downed while we + * are polling. */ + +poll_some_more: + if (ep93xx_rx(dev, budget)) + return 1; + + netif_rx_complete(dev); + + spin_lock_irq(&ep->rx_lock); + wrl(ep, 0x0024, 0x0000000f); + if (ep93xx_have_more_rx(ep)) { + wrl(ep, 0x0024, 0x00000008); + wrl(ep, 0x0028, 0x00000004); + spin_unlock_irq(&ep->rx_lock); + + if (netif_rx_reschedule(dev, 0)) + goto poll_some_more; + + return 0; + } + spin_unlock_irq(&ep->rx_lock); + + return 0; +} + +static int ep93xx_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + int entry; + + if (unlikely(skb->len) > MAX_PKT_SIZE) { + ep->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + + entry = ep->tx_pointer; + ep->tx_pointer = (ep->tx_pointer + 1) % TX_QUEUE_ENTRIES; + + ep->descs->tdesc[entry].tdesc1 = + TDESC1_EOF | (entry << 16) | (skb->len & 0xfff); + skb_copy_and_csum_dev(skb, ep->tx_buf[entry]); + dma_sync_single(NULL, ep->descs->tdesc[entry].buf_addr, + skb->len, DMA_TO_DEVICE); + dev_kfree_skb(skb); + + dev->trans_start = jiffies; + + spin_lock_irq(&ep->tx_pending_lock); + ep->tx_pending++; + if (ep->tx_pending == TX_QUEUE_ENTRIES) + netif_stop_queue(dev); + spin_unlock_irq(&ep->tx_pending_lock); + + wrl(ep, 0x00bc, 1); + + return 0; +} + +static void ep93xx_tx_complete(struct net_device *dev) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + int tail_offset; + int wake; + + tail_offset = rdl(ep, 0x00c8) - ep->descs_dma_addr; + wake = 0; + + spin_lock(&ep->tx_pending_lock); + while (1) { + int entry; + struct ep93xx_tstat *tstat; + u32 tstat0; + + entry = ep->tx_clean_pointer; + tstat = ep->descs->tstat + entry; + if ((void *)tstat - (void *)ep->descs == tail_offset) + break; + + tstat0 = tstat->tstat0; + tstat->tstat0 = 0; + + if (!(tstat0 & TSTAT0_TXFP)) { + printk(KERN_CRIT "ep93xx_tx_complete: buffer not done " + " %.8x\n", tstat0); + BUG(); + } + if (tstat0 & TSTAT0_FA) { + printk(KERN_CRIT "ep93xx_tx_complete: frame aborted " + " %.8x\n", tstat0); + BUG(); + } + if ((tstat0 & TSTAT0_BUFFER_INDEX) != entry) { + printk(KERN_CRIT "ep93xx_tx_complete: entry mismatch " + " %.8x\n", tstat0); + BUG(); + } + + if (tstat0 & TSTAT0_TXWE) { + int length = ep->descs->tdesc[entry].tdesc1 & 0xfff; + + ep->stats.tx_packets++; + ep->stats.tx_bytes += length; + } else { + ep->stats.tx_errors++; + } +// if (tstat0 & TSTAT0_LCRS) +// ep->stats.tx_carrier_errors++; + if (tstat0 & TSTAT0_OW) + ep->stats.tx_window_errors++; + if (tstat0 & TSTAT0_TXU) + ep->stats.tx_fifo_errors++; + ep->stats.collisions += (tstat0 >> 16) & 0x1f; + + ep->tx_clean_pointer = (entry + 1) % TX_QUEUE_ENTRIES; + if (ep->tx_pending == TX_QUEUE_ENTRIES) + wake = 1; + ep->tx_pending--; + } + spin_unlock(&ep->tx_pending_lock); + + if (wake) + netif_wake_queue(dev); +} + +static irqreturn_t ep93xx_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct ep93xx_priv *ep = netdev_priv(dev); + u32 status; + + status = rdl(ep, 0x002c); + if (status == 0) + return IRQ_NONE; + + if (status & 0x00000004) { + spin_lock(&ep->rx_lock); + if (likely(__netif_rx_schedule_prep(dev))) { + wrl(ep, 0x0024, 0x00000008); + __netif_rx_schedule(dev); + } + spin_unlock(&ep->rx_lock); + } + + if (status & 0x00000008) + ep93xx_tx_complete(dev); + + return IRQ_HANDLED; +} + +static void ep93xx_free_buffers(struct ep93xx_priv *ep) +{ + int i; + + for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) { + dma_addr_t d; + + d = ep->descs->rdesc[i].buf_addr; + if (d) + dma_unmap_single(NULL, d, PAGE_SIZE, DMA_FROM_DEVICE); + + if (ep->rx_buf[i] != NULL) + free_page((unsigned long)ep->rx_buf[i]); + } + + for (i = 0; i < TX_QUEUE_ENTRIES; i += 2) { + dma_addr_t d; + + d = ep->descs->tdesc[i].buf_addr; + if (d) + dma_unmap_single(NULL, d, PAGE_SIZE, DMA_TO_DEVICE); + + if (ep->tx_buf[i] != NULL) + free_page((unsigned long)ep->tx_buf[i]); + } + + dma_free_coherent(NULL, sizeof(struct ep93xx_descs), ep->descs, + ep->descs_dma_addr); +} + +/* + * The hardware enforces a sub-2K maximum packet size, so we put + * two buffers on every hardware page. + */ +static int ep93xx_alloc_buffers(struct ep93xx_priv *ep) +{ + int i; + + ep->descs = dma_alloc_coherent(NULL, sizeof(struct ep93xx_descs), + &ep->descs_dma_addr, GFP_KERNEL | GFP_DMA); + if (ep->descs == NULL) + return 1; + + for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) { + void *page; + dma_addr_t d; + + page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (page == NULL) + goto err; + + d = dma_map_single(NULL, page, PAGE_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(d)) { + free_page((unsigned long)page); + goto err; + } + + ep->rx_buf[i] = page; + ep->descs->rdesc[i].buf_addr = d; + ep->descs->rdesc[i].rdesc1 = (i << 16) | PKT_BUF_SIZE; + + ep->rx_buf[i + 1] = page + PKT_BUF_SIZE; + ep->descs->rdesc[i + 1].buf_addr = d + PKT_BUF_SIZE; + ep->descs->rdesc[i + 1].rdesc1 = ((i + 1) << 16) | PKT_BUF_SIZE; + } + + for (i = 0; i < TX_QUEUE_ENTRIES; i += 2) { + void *page; + dma_addr_t d; + + page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (page == NULL) + goto err; + + d = dma_map_single(NULL, page, PAGE_SIZE, DMA_TO_DEVICE); + if (dma_mapping_error(d)) { + free_page((unsigned long)page); + goto err; + } + + ep->tx_buf[i] = page; + ep->descs->tdesc[i].buf_addr = d; + + ep->tx_buf[i + 1] = page + PKT_BUF_SIZE; + ep->descs->tdesc[i + 1].buf_addr = d + PKT_BUF_SIZE; + } + + return 0; + +err: + ep93xx_free_buffers(ep); + return 1; +} + +static void ep93xx_start_hw(struct net_device *dev) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + unsigned long addr; + + wrl(ep, 0x0020, 0x00000001); + while (rdl(ep, 0x0020) & 0x00000001) + cpu_relax(); + wrl(ep, 0x0020, ((ep->mdc_divisor - 1) << 9) | + (ep->mdc_preamble_suppress << 8)); + + /* Receive descriptor ring. */ + addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rdesc); + wrl(ep, 0x0090, addr); + wrl(ep, 0x0098, addr); + wrw(ep, 0x0094, RX_QUEUE_ENTRIES * sizeof(struct ep93xx_rdesc)); + + /* Receive status ring. */ + addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rstat); + wrl(ep, 0x00a0, addr); + wrl(ep, 0x00a8, addr); + wrw(ep, 0x00a4, RX_QUEUE_ENTRIES * sizeof(struct ep93xx_rstat)); + + /* Transmit descriptor ring. */ + addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, tdesc); + wrl(ep, 0x00b0, addr); + wrl(ep, 0x00b8, addr); + wrw(ep, 0x00b4, TX_QUEUE_ENTRIES * sizeof(struct ep93xx_tdesc)); + + /* Transmit status ring. */ + addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, tstat); + wrl(ep, 0x00c0, addr); + wrl(ep, 0x00c8, addr); + wrw(ep, 0x00c4, TX_QUEUE_ENTRIES * sizeof(struct ep93xx_tstat)); + + wrl(ep, 0x0080, 0x00000101); // bmctl + wrl(ep, 0x0024, 0x0000000f); // inten + wrl(ep, 0x0064, 0x00000000); // gintmask + while (!(rdl(ep, 0x0084) & 0x00000008)) + cpu_relax(); + wrl(ep, 0x009c, RX_QUEUE_ENTRIES); + wrl(ep, 0x00ac, RX_QUEUE_ENTRIES); + + wrb(ep, 0x0050, dev->dev_addr[0]); + wrb(ep, 0x0051, dev->dev_addr[1]); + wrb(ep, 0x0052, dev->dev_addr[2]); + wrb(ep, 0x0053, dev->dev_addr[3]); + wrb(ep, 0x0054, dev->dev_addr[4]); + wrb(ep, 0x0055, dev->dev_addr[5]); + wrl(ep, 0x004c, 0x00000000); + + wrl(ep, 0x00e8, (MAX_PKT_SIZE << 16) | MAX_PKT_SIZE); + + wrl(ep, 0x0000, 0x00073800); + wrl(ep, 0x0004, 0x00000001); + + wrl(ep, 0x0008, 0x00000040); +} + +static void ep93xx_stop_hw(struct net_device *dev) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + + wrl(ep, 0x0020, 0x00000001); + while (rdl(ep, 0x0020) & 0x00000001) + cpu_relax(); +} + +static int ep93xx_open(struct net_device *dev) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + int err; + + if (ep93xx_alloc_buffers(ep)) + return -ENOMEM; + + if (!memcmp(dev->dev_addr, "\x00\x00\x00\x00\x00\x00", 6)) { + 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]); + } + + ep93xx_start_hw(dev); + + spin_lock_init(&ep->rx_lock); + ep->rx_pointer = 0; + ep->tx_clean_pointer = 0; + ep->tx_pointer = 0; + spin_lock_init(&ep->tx_pending_lock); + ep->tx_pending = 0; + + err = request_irq(ep->irq, ep93xx_irq, SA_SHIRQ, "ep93xx_eth", dev); + if (err) { + ep93xx_stop_hw(dev); + ep93xx_free_buffers(ep); + return err; + } + + wrl(ep, 0x0064, 0x00008000); // gintmask + + netif_start_queue(dev); + + return 0; +} + +static int ep93xx_close(struct net_device *dev) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + + netif_stop_queue(dev); + + wrl(ep, 0x0064, 0x00000000); // gintmask + free_irq(ep->irq, dev); + ep93xx_stop_hw(dev); + ep93xx_free_buffers(ep); + + return 0; +} + +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); +} + +static int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + int data; + + wrl(ep, 0x0010, 0x8000 | (phy_id << 5) | reg); + while (rdl(ep, 0x0018) & 1) + cpu_relax(); + data = rdl(ep, 0x0014); + +// printk(KERN_INFO "mdio_read %d %d -> %.4x\n", phy_id, reg, data); + + return data; +} + +static void ep93xx_mdio_write(struct net_device *dev, int phy_id, int reg, int data) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + +// printk(KERN_INFO "mdio_write %d %d %.4x\n", phy_id, reg, data); + + wrl(ep, 0x0014, data); + wrl(ep, 0x0010, 0x4000 | (phy_id << 5) | reg); + while (rdl(ep, 0x0018) & 1) + cpu_relax(); +} + +static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_MODULE_NAME); + strcpy(info->version, DRV_MODULE_VERSION); +} + +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); +} + +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); +} + +static int ep93xx_nway_reset(struct net_device *dev) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + return mii_nway_restart(&ep->mii); +} + +static u32 ep93xx_get_link(struct net_device *dev) +{ + struct ep93xx_priv *ep = netdev_priv(dev); + return mii_link_ok(&ep->mii); +} + +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, +}; + +struct net_device *ep93xx_dev_alloc(struct ep93xx_eth_data *data) +{ + struct net_device *dev; + struct ep93xx_priv *ep; + + dev = alloc_etherdev(sizeof(struct ep93xx_priv)); + if (dev == NULL) + return NULL; + ep = netdev_priv(dev); + + memcpy(dev->dev_addr, data->dev_addr, ETH_ALEN); + + dev->get_stats = ep93xx_get_stats; + dev->ethtool_ops = &ep93xx_ethtool_ops; + dev->poll = ep93xx_poll; + dev->hard_start_xmit = ep93xx_xmit; + dev->open = ep93xx_open; + dev->stop = ep93xx_close; + dev->do_ioctl = ep93xx_ioctl; + + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; + dev->weight = 64; + + return dev; +} + + +static int ep93xx_eth_remove(struct platform_device *pdev) +{ + struct net_device *dev; + struct ep93xx_priv *ep; + + dev = platform_get_drvdata(pdev); + if (dev == NULL) + return 0; + platform_set_drvdata(pdev, NULL); + + ep = netdev_priv(dev); + + // @@@ force down + unregister_netdev(dev); + ep93xx_free_buffers(ep); + + if (ep->base_addr != NULL) + iounmap(ep->base_addr); + + if (ep->res != NULL) { + release_resource(ep->res); + kfree(ep->res); + } + + free_netdev(dev); + + 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; + + data = pdev->dev.platform_data; + if (pdev == NULL) + return -ENODEV; + + dev = ep93xx_dev_alloc(data); + if (dev == NULL) { + err = -ENOMEM; + goto err_out; + } + ep = netdev_priv(dev); + + platform_set_drvdata(pdev, dev); + + ep->res = request_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1, + pdev->dev.bus_id); + if (ep->res == NULL) { + dev_err(&pdev->dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto err_out; + } + + ep->base_addr = ioremap(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start); + if (ep->base_addr == NULL) { + dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n"); + err = -EIO; + goto err_out; + } + 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; + ep->mdc_divisor = 40; // max hclk 100 MHz, min MDIO clk 2.5 MHz + ep->mdc_preamble_suppress = data->mdc_preamble_suppress; + + err = register_netdev(dev); + if (err) { + dev_err(&pdev->dev, "Failed to register netdev\n"); + goto err_out; + } + + 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]); + + return 0; + +err_out: + ep93xx_eth_remove(pdev); + return err; +} + + +static struct platform_driver ep93xx_eth_driver = { + .probe = ep93xx_eth_probe, + .remove = ep93xx_eth_remove, + .driver = { + .name = "ep93xx-eth", + }, +}; + +static int __init ep93xx_eth_init_module(void) +{ + printk(KERN_INFO DRV_MODULE_NAME " version " DRV_MODULE_VERSION " loading\n"); + return platform_driver_register(&ep93xx_eth_driver); +} + +static void __exit ep93xx_eth_cleanup_module(void) +{ + platform_driver_unregister(&ep93xx_eth_driver); +} + +module_init(ep93xx_eth_init_module); +module_exit(ep93xx_eth_cleanup_module); +MODULE_LICENSE("GPL"); Index: linux-2.6.18-rc1-git9/drivers/net/arm/ep93xx_eth.h =================================================================== --- /dev/null +++ linux-2.6.18-rc1-git9/drivers/net/arm/ep93xx_eth.h @@ -0,0 +1,75 @@ +/* + * EP93xx ethernet network device driver + * Copyright (C) 2006 Lennert Buytenhek + * Dedicated to Marija Kulikova. + * + * 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. + */ + +#ifndef __EP93XX_ETH_H +#define __EP93XX_ETH_H + +struct ep93xx_rdesc +{ + u32 buf_addr; + u32 rdesc1; +}; + +#define RDESC1_NSOF 0x80000000 +#define RDESC1_BUFFER_INDEX 0x7fff0000 +#define RDESC1_BUFFER_LENGTH 0x0000ffff + +struct ep93xx_rstat +{ + u32 rstat0; + u32 rstat1; +}; + +#define RSTAT0_RFP 0x80000000 +#define RSTAT0_RWE 0x40000000 +#define RSTAT0_EOF 0x20000000 +#define RSTAT0_EOB 0x10000000 +#define RSTAT0_AM 0x00c00000 +#define RSTAT0_RX_ERR 0x00200000 +#define RSTAT0_OE 0x00100000 +#define RSTAT0_FE 0x00080000 +#define RSTAT0_RUNT 0x00040000 +#define RSTAT0_EDATA 0x00020000 +#define RSTAT0_CRCE 0x00010000 +#define RSTAT0_CRCI 0x00008000 +#define RSTAT0_HTI 0x00003f00 +#define RSTAT1_RFP 0x80000000 +#define RSTAT1_BUFFER_INDEX 0x7fff0000 +#define RSTAT1_FRAME_LENGTH 0x0000ffff + +struct ep93xx_tdesc +{ + u32 buf_addr; + u32 tdesc1; +}; + +#define TDESC1_EOF 0x80000000 +#define TDESC1_BUFFER_INDEX 0x7fff0000 +#define TDESC1_BUFFER_ABORT 0x00008000 +#define TDESC1_BUFFER_LENGTH 0x00000fff + +struct ep93xx_tstat +{ + u32 tstat0; +}; + +#define TSTAT0_TXFP 0x80000000 +#define TSTAT0_TXWE 0x40000000 +#define TSTAT0_FA 0x20000000 +#define TSTAT0_LCRS 0x10000000 +#define TSTAT0_OW 0x04000000 +#define TSTAT0_TXU 0x02000000 +#define TSTAT0_ECOLL 0x01000000 +#define TSTAT0_NCOLL 0x001f0000 +#define TSTAT0_BUFFER_INDEX 0x00007fff + + +#endif Index: linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/ts72xx.c =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/mach-ep93xx/ts72xx.c +++ linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/ts72xx.c @@ -157,12 +157,41 @@ static struct platform_device ts72xx_rtc .num_resources = 0, }; +static struct ep93xx_eth_data ts72xx_eth_data = { +}; + +static struct resource ts72xx_eth_resource[] = { + { + .start = EP93XX_ETHERNET_PHYS_BASE, + .end = EP93XX_ETHERNET_PHYS_BASE + 0xffff, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_EP93XX_ETHERNET, + .end = IRQ_EP93XX_ETHERNET, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device ts72xx_eth_device = { + .name = "ep93xx-eth", + .id = -1, + .dev = { + .platform_data = &ts72xx_eth_data, + }, + .num_resources = 2, + .resource = ts72xx_eth_resource, +}; + static void __init ts72xx_init_machine(void) { ep93xx_init_devices(); if (board_is_ts7200()) platform_device_register(&ts72xx_flash); platform_device_register(&ts72xx_rtc_device); + + memcpy(ts72xx_eth_data.dev_addr, + (void *)(EP93XX_ETHERNET_BASE + 0x50), 6); + platform_device_register(&ts72xx_eth_device); } MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC") Index: linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/ep93xx-regs.h =================================================================== --- linux-2.6.18-rc1-git9.orig/include/asm-arm/arch-ep93xx/ep93xx-regs.h +++ linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/ep93xx-regs.h @@ -27,6 +27,7 @@ #define EP93XX_DMA_BASE (EP93XX_AHB_VIRT_BASE + 0x00000000) #define EP93XX_ETHERNET_BASE (EP93XX_AHB_VIRT_BASE + 0x00010000) +#define EP93XX_ETHERNET_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x00010000) #define EP93XX_USB_BASE (EP93XX_AHB_VIRT_BASE + 0x00020000) #define EP93XX_USB_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x00020000) Index: linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/platform.h =================================================================== --- linux-2.6.18-rc1-git9.orig/include/asm-arm/arch-ep93xx/platform.h +++ linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/platform.h @@ -11,5 +11,17 @@ void ep93xx_init_devices(void); void ep93xx_clock_init(void); extern struct sys_timer ep93xx_timer; +struct ep93xx_eth_data +{ + unsigned char dev_addr[6]; + unsigned char phy_id; + unsigned char mdc_preamble_suppress; +}; + +struct ep93xx_i2c_pins { + unsigned long sda_pin; + unsigned long scl_pin; +}; + #endif Index: linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/edb9315a.c =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/mach-ep93xx/edb9315a.c +++ linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/edb9315a.c @@ -44,10 +44,41 @@ static struct platform_device edb9315a_f .resource = &edb9315a_flash_resource, }; +static struct ep93xx_eth_data edb9315a_eth_data = { + .phy_id = 1, + .mdc_preamble_suppress = 0, +}; + +static struct resource edb9315a_eth_resource[] = { + { + .start = EP93XX_ETHERNET_PHYS_BASE, + .end = EP93XX_ETHERNET_PHYS_BASE + 0xffff, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_EP93XX_ETHERNET, + .end = IRQ_EP93XX_ETHERNET, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device edb9315a_eth_device = { + .name = "ep93xx-eth", + .id = -1, + .dev = { + .platform_data = &edb9315a_eth_data, + }, + .num_resources = 2, + .resource = edb9315a_eth_resource, +}; + static void __init edb9315a_init_machine(void) { ep93xx_init_devices(); platform_device_register(&edb9315a_flash); + + memcpy(edb9315a_eth_data.dev_addr, + (void *)(EP93XX_ETHERNET_BASE + 0x50), 6); + platform_device_register(&edb9315a_eth_device); } MACHINE_START(EDB9315A, "Cirrus Logic EDB9315A Evaluation Board") Index: linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/gesbc9312.c =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/mach-ep93xx/gesbc9312.c +++ linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/gesbc9312.c @@ -43,10 +43,38 @@ static struct platform_device gesbc9312_ .resource = &gesbc9312_flash_resource, }; +static struct ep93xx_eth_data gesbc9312_eth_data = { + .phy_id = 1, + .mdc_preamble_suppress = 0, +}; + +static struct resource gesbc9312_eth_resource[] = { + { + .start = EP93XX_ETHERNET_PHYS_BASE, + .end = EP93XX_ETHERNET_PHYS_BASE + 0xffff, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_EP93XX_ETHERNET, + .end = IRQ_EP93XX_ETHERNET, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device gesbc9312_eth_device = { + .name = "ep93xx-eth", + .id = -1, + .dev = { + .platform_data = &gesbc9312_eth_data, + }, + .num_resources = 2, + .resource = gesbc9312_eth_resource, +}; + static void __init gesbc9312_init_machine(void) { ep93xx_init_devices(); platform_device_register(&gesbc9312_flash); + platform_device_register(&gesbc9312_eth_device); } MACHINE_START(GESBC9312, "Glomation GESBC-9312-sx") Index: linux-2.6.18-rc1-git9/arch/arm/Kconfig =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/Kconfig +++ linux-2.6.18-rc1-git9/arch/arm/Kconfig @@ -97,6 +97,9 @@ config VECTORS_BASE help The base address of exception vectors. +config RUNTIME_PHYS_OFFSET + bool + source "init/Kconfig" menu "System Type" @@ -171,6 +174,7 @@ config ARCH_EP93XX bool "EP93xx-based" select ARM_AMBA select ARM_VIC + select RUNTIME_PHYS_OFFSET help This enables support for the Cirrus EP93xx series of CPUs. Index: linux-2.6.18-rc1-git9/arch/arm/boot/compressed/Makefile =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/boot/compressed/Makefile +++ linux-2.6.18-rc1-git9/arch/arm/boot/compressed/Makefile @@ -74,12 +74,11 @@ targets := vmlinux vmlinux.lds pig EXTRA_CFLAGS := -fpic EXTRA_AFLAGS := -# Supply ZRELADDR, INITRD_PHYS and PARAMS_PHYS to the decompressor via -# linker symbols. We only define initrd_phys and params_phys if the -# machine class defined the corresponding makefile variable. +# Supply ZRELADDR and PARAMS_PHYS to the decompressor via linker +# symbols. We only define params_phys if the machine class defined +# the corresponding makefile variable. +ifneq ($(ZRELADDR),) LDFLAGS_vmlinux := --defsym zreladdr=$(ZRELADDR) -ifneq ($(INITRD_PHYS),) -LDFLAGS_vmlinux += --defsym initrd_phys=$(INITRD_PHYS) endif ifneq ($(PARAMS_PHYS),) LDFLAGS_vmlinux += --defsym params_phys=$(PARAMS_PHYS) Index: linux-2.6.18-rc1-git9/arch/arm/boot/compressed/head.S =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/boot/compressed/head.S +++ linux-2.6.18-rc1-git9/arch/arm/boot/compressed/head.S @@ -149,7 +149,15 @@ not_angel: .text adr r0, LC0 +#ifdef CONFIG_RUNTIME_PHYS_OFFSET + ldmia r0, {r1, r2, r3, r5, r6, ip, sp} + and r4, pc, #0xf0000000 +// add r4, r4, #TEXT_OFFSET + add r4, r4, #0x00008000 +#else ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp} +#endif + subs r0, r0, r1 @ calculate the delta offset @ if delta is zero, we are @@ -281,7 +289,9 @@ wont_overwrite: mov r0, r4 LC0: .word LC0 @ r1 .word __bss_start @ r2 .word _end @ r3 +#ifndef CONFIG_RUNTIME_PHYS_OFFSET .word zreladdr @ r4 +#endif .word _start @ r5 .word _got_start @ r6 .word _got_end @ ip Index: linux-2.6.18-rc1-git9/arch/arm/kernel/head.S =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/kernel/head.S +++ linux-2.6.18-rc1-git9/arch/arm/kernel/head.S @@ -39,8 +39,8 @@ .globl swapper_pg_dir .equ swapper_pg_dir, KERNEL_RAM_ADDR - 0x4000 - .macro pgtbl, rd - ldr \rd, =(__virt_to_phys(KERNEL_RAM_ADDR - 0x4000)) + .macro pgtbl, rd, phys_offset + add \rd, \phys_offset, #(TEXT_OFFSET - 0x4000) .endm #ifdef CONFIG_XIP_KERNEL @@ -202,10 +202,20 @@ __turn_mmu_on: * Returns: * r0, r3, r6, r7 corrupted * r4 = physical page table address + * r5 = PHYS_OFFSET */ .type __create_page_tables, %function __create_page_tables: - pgtbl r4 @ page table address +#ifdef CONFIG_RUNTIME_PHYS_OFFSET + adr r5, stext @ r5 = phys_offset + sub r5, r5, #TEXT_OFFSET + ldr r4, =phys_offset + str r5, [r4] +#else + mov r5, #PHYS_OFFSET @ r5 = phys_offset +#endif + + pgtbl r4, r5 @ r4 = page table address /* * Clear the 16K level 1 swapper page table @@ -251,7 +261,7 @@ __create_page_tables: * Then map first 1MB of ram in case it contains our boot params. */ add r0, r4, #PAGE_OFFSET >> 18 - orr r6, r7, #PHYS_OFFSET + orr r6, r7, r5 str r6, [r0] #ifdef CONFIG_XIP_KERNEL Index: linux-2.6.18-rc1-git9/arch/arm/kernel/setup.c =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/kernel/setup.c +++ linux-2.6.18-rc1-git9/arch/arm/kernel/setup.c @@ -59,6 +59,16 @@ extern void reboot_setup(char *str); extern int root_mountflags; extern void _stext, _text, _etext, __data_start, _edata, _end; +#ifdef CONFIG_RUNTIME_PHYS_OFFSET +/* + * The assignment is here solely to prevent this variable from ending + * up in bss. As the early startup code writes to it, we don't want it + * to be zeroed again later. + */ +unsigned long phys_offset = 0xdeadbeef; +EXPORT_SYMBOL(phys_offset); +#endif + unsigned int processor_id; unsigned int __machine_arch_type; EXPORT_SYMBOL(__machine_arch_type); @@ -749,7 +759,7 @@ static struct init_tags { { tag_size(tag_core), ATAG_CORE }, { 1, PAGE_SIZE, 0xff }, { tag_size(tag_mem32), ATAG_MEM }, - { MEM_SIZE, PHYS_OFFSET }, + { MEM_SIZE, 0 }, { 0, ATAG_NONE } }; @@ -770,6 +780,8 @@ void __init setup_arch(char **cmdline_p) struct machine_desc *mdesc; char *from = default_command_line; + init_tags.mem.start = PHYS_OFFSET; + setup_processor(); mdesc = setup_machine(machine_arch_type); machine_name = mdesc->name; Index: linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/Makefile.boot =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/mach-ep93xx/Makefile.boot +++ linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/Makefile.boot @@ -1,2 +1 @@ - zreladdr-y := 0x00008000 -params_phys-y := 0x00000100 +# zreladdr-y := 0xc0008000 Index: linux-2.6.18-rc1-git9/include/asm-arm/memory.h =================================================================== --- linux-2.6.18-rc1-git9.orig/include/asm-arm/memory.h +++ linux-2.6.18-rc1-git9/include/asm-arm/memory.h @@ -73,6 +73,14 @@ */ #define IOREMAP_MAX_ORDER 24 +/* + * PHYS_OFFSET determined at run time? + */ +#if defined(CONFIG_RUNTIME_PHYS_OFFSET) && !defined(__ASSEMBLY__) +extern unsigned long phys_offset; +#define PHYS_OFFSET (phys_offset) +#endif + #else /* CONFIG_MMU */ /* Index: linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/memory.h =================================================================== --- linux-2.6.18-rc1-git9.orig/include/asm-arm/arch-ep93xx/memory.h +++ linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/memory.h @@ -5,7 +5,9 @@ #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H +#ifndef CONFIG_RUNTIME_PHYS_OFFSET #define PHYS_OFFSET UL(0x00000000) +#endif #define __bus_to_virt(x) __phys_to_virt(x) #define __virt_to_bus(x) __virt_to_phys(x) Index: linux-2.6.18-rc1-git9/arch/arm/common/vic.c =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/common/vic.c +++ linux-2.6.18-rc1-git9/arch/arm/common/vic.c @@ -59,9 +59,7 @@ void __init vic_init(void __iomem *base, /* Disable all interrupts initially. */ writel(0, base + VIC_INT_SELECT); - writel(0, base + VIC_INT_ENABLE); writel(~0, base + VIC_INT_ENABLE_CLEAR); - writel(0, base + VIC_IRQ_STATUS); writel(0, base + VIC_ITCR); writel(~0, base + VIC_INT_SOFT_CLEAR); @@ -78,7 +76,8 @@ void __init vic_init(void __iomem *base, for (i = 0; i < 16; i++) { void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); - writel(VIC_VECT_CNTL_ENABLE | i, reg); +// writel(VIC_VECT_CNTL_ENABLE | i, reg); + writel(0, reg); } writel(32, base + VIC_DEF_VECT_ADDR); Index: linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/Kconfig =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/mach-ep93xx/Kconfig +++ linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/Kconfig @@ -33,6 +33,12 @@ config MACH_GESBC9312 Say 'Y' here if you want your kernel to support the Glomation GESBC-9312-sx board. +config MACH_MICRO9 + bool "Support Contec Hypercontrol Micro9" + help + Say 'Y' here if you want your kernel to support the + Contec Hypercontrol Micro9 development board. + config MACH_TS72XX bool "Support Technologic Systems TS-72xx SBC" help Index: linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/Makefile =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/mach-ep93xx/Makefile +++ linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/Makefile @@ -10,4 +10,5 @@ obj-$(CONFIG_MACH_EDB9302) += edb9302.o obj-$(CONFIG_MACH_EDB9315) += edb9315.o obj-$(CONFIG_MACH_EDB9315A) += edb9315a.o obj-$(CONFIG_MACH_GESBC9312) += gesbc9312.o +obj-$(CONFIG_MACH_MICRO9) += micro9.o obj-$(CONFIG_MACH_TS72XX) += ts72xx.o Index: linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/micro9.c =================================================================== --- /dev/null +++ linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/micro9.c @@ -0,0 +1,252 @@ +/* + * linux/arch/arm/mach-ep93xx/micro9.c + * + * Copyright (C) 2004 Contec Steuerungstechnik & Automation GmbH + * Manfred Gruber + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* + * FPGA extension board + */ +#ifdef CONFIG_MICRO9_FPGA_EXTENSION_BOARD +#include +#endif + +#ifdef CONFIG_ARCH_EP93XX_GPIO_IRQ +#ifdef CONFIG_GENERIC_HARDIRQS +#include +#endif +#include "ep93xx_gpio_irq.h" +#endif + +#ifdef CONFIG_LEDS + +#define LED_STATE_ENABLED 1 +#define LED_STATE_CLAIMED 2 + +static unsigned int led_state; +static unsigned int hw_led_state; + +#define LED_ON 1 + +void micro9_leds_event(led_event_t evt) +{ + unsigned long flags; + +/* Has to be checked if wee need locking here, because of locking in gpio code */ +#ifdef CONFIG_PREEMPT_RT + raw_local_irq_save(flags); +#else + local_irq_save(flags); +#endif + switch (evt) { + case led_start: + led_state = LED_STATE_ENABLED; + hw_led_state = LED_ON ; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + hw_led_state = 0; + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = LED_ON; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = 0; + break; + + case led_timer: + break; + + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_ON; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_ON; + break; + + case led_halted: + break; + + case led_amber_on: + break; + + case led_amber_off: + break; + + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= LED_ON; + break; + + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~LED_ON; + break; + + default: + break; + } + + gpio_line_config(EP93XX_GPIO_LINE_E(1), GPIO_OUT); + + if (led_state & LED_STATE_ENABLED) + { + if(hw_led_state) + gpio_line_set(EP93XX_GPIO_LINE_E(1), EP93XX_GPIO_HIGH); + else + gpio_line_set(EP93XX_GPIO_LINE_E(1), EP93XX_GPIO_LOW); + } + +/* Has to be checked if wee need locking here, because of locking in gpio code */ +#ifdef CONFIG_PREEMPT_RT + raw_local_irq_restore(flags); +#else + local_irq_restore(flags); +#endif +} + +#else +#define micro9_leds_event 0 +#endif + +static void __init micro9_init_machine(void) +{ + ep93xx_init_devices(); + +#ifdef CONFIG_MTD_PHYSMAP + physmap_configure(0x10000000 , SZ_64M , 4, NULL); + /* OLD platform_device_register(&cfi_flash_device); */ +#endif + + /* + * CAN memory, can't be used by other things. + */ + if (!request_mem_region(MICRO9_CAN1_IO_START, MICRO9_CAN1_IO_LEN, + "OKI CAN 1")) { + printk(KERN_ERR "OKI CAN1: unable to reserve memory for " + "OKI can1 controller\n"); + } + + if (!request_mem_region(MICRO9_CAN2_IO_START, MICRO9_CAN2_IO_LEN, + "OKI CAN 2")) { + printk(KERN_ERR "OKI CAN2: unable to reserve memory for " + "OKI can2 controller\n"); + } + +#ifdef CONFIG_MICRO9_FPGA_EXTENSION_BOARD + /* + * FPGA extension board. + */ + if (!request_mem_region(FPGA_IO_START_1, FPGA_IO_LEN_1, "FPGA 1")) { + printk(KERN_ERR "FPGA: unable to reserve memory 1 for " + "Micro9 FPGA extension board\n"); + } + + if (!request_mem_region(FPGA_IO_START_2, FPGA_IO_LEN_2, "FPGA 2")) { + printk(KERN_ERR "FPGA: unable to reserve memory 2 for " + "Micro9 FPGA extension board\n"); + } +#endif + +#ifdef CONFIG_LEDS + leds_event = micro9_leds_event; + leds_event(led_start); +#endif + +#ifdef CONFIG_RASTER_BUS_ARBITRATION + /* This is run default with a display, bad latencies because + * usb, mac, dma is allowed to take bus also when irq is active + * bus prio 1 raster + * bus prio 2 raster cursor + * bus prio 3 mac + * bus prio 4 usb + * bus prio 5 core + * bus prio 6 dma + */ + outl(0x1, SYSCON_BMAR); +#endif + +#ifdef CONFIG_IRQ_BUS_ARBITRATION + /* This is run on PREEMPT_RT with a display, not so well latencies + * but a lot better than CONFIG_RASTER_BUS_ARBITRATION + * bus accesss for usb, mac, dma is not allowed when a irq is active + * core has not the highest prio + * bus prio 1 raster + * bus prio 2 raster cursor + * bus prio 3 mac + * bus prio 4 usb + * bus prio 5 core + * bus prio 6 dma + */ + outl(0x151, SYSCON_BMAR); +#endif + +#ifdef CONFIG_CORE_IRQ_BUS_ARBITRATION + /* This is run on PREEMPT_RT without a display, good latencies + * core has highest prio !!! + * don't allow usb, mac, dma to bus if irq is active !! + * bus prio 1 raster + * bus prio 2 raster cursor + * bus prio 3 mac + * bus prio 4 usb + * bus prio 5 core + * bus prio 6 dma + */ + outl(0x159, SYSCON_BMAR); +#endif +} + +MACHINE_START(MICRO9, "Contec Hypercontrol Micro9") + /* Maintainer: Manfred Gruber */ + .phys_io = EP93XX_APB_PHYS_BASE, + .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, + .boot_params = 0x00000100, + .map_io = ep93xx_map_io, + .init_irq = ep93xx_init_irq, + .timer = &ep93xx_timer, + .init_machine = micro9_init_machine, +MACHINE_END Index: linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/hardware.h =================================================================== --- linux-2.6.18-rc1-git9.orig/include/asm-arm/arch-ep93xx/hardware.h +++ linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/hardware.h @@ -9,4 +9,5 @@ #include "platform.h" #include "gesbc9312.h" +#include "micro9.h" #include "ts72xx.h" Index: linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/micro9.h =================================================================== --- /dev/null +++ linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/micro9.h @@ -0,0 +1,8 @@ +/* + * linux/include/asm-arm/arch-ep93xx/micro9.h + */ + +#define MICRO9_CAN1_IO_START 0x20000000 +#define MICRO9_CAN1_IO_LEN 0x01000000 +#define MICRO9_CAN2_IO_START 0x21000000 +#define MICRO9_CAN2_IO_LEN 0x01000000 Index: linux-2.6.18-rc1-git9/drivers/i2c/busses/Kconfig =================================================================== --- linux-2.6.18-rc1-git9.orig/drivers/i2c/busses/Kconfig +++ linux-2.6.18-rc1-git9/drivers/i2c/busses/Kconfig @@ -537,4 +537,16 @@ config I2C_MV64XXX This driver can also be built as a module. If so, the module will be called i2c-mv64xxx. +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.18-rc1-git9/drivers/i2c/busses/Makefile =================================================================== --- linux-2.6.18-rc1-git9.orig/drivers/i2c/busses/Makefile +++ linux-2.6.18-rc1-git9/drivers/i2c/busses/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o 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.18-rc1-git9/drivers/i2c/busses/i2c-ep93xx.c =================================================================== --- /dev/null +++ linux-2.6.18-rc1-git9/drivers/i2c/busses/i2c-ep93xx.c @@ -0,0 +1,174 @@ +/* + * Intel's IXP4xx XScale NPU chipsets (IXP420, 421, 422, 425) do not have + * an on board I2C controller but provide 16 GPIO pins that are often + * used to create an I2C bus. This driver provides an i2c_adapter + * interface that plugs in under algo_bit and drives the GPIO pins + * as instructed by the alogorithm driver. + * + * Based on i2x-ixp4xx.c + * Author: Deepak Saxena + * Copyright (c) 2003-2004 MontaVista Software Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * NOTE: Since different platforms will use different GPIO pins for + * I2C, this driver uses an ep93xx specific platform_data + * pointer to pass the GPIO numbers to the driver. This + * allows us to support all the different boards + * w/o having to put #ifdefs in this driver. + * + * See arch/arm/mach-ep93xx/core.c for an example of building a + * device list and filling in the ep93xx_i2c_pins data structure + * that is passed as the platform_data to this driver. + */ + +#include +#include +#include +#include +#include + +#include +#include + +static inline int ep93xx_scl_pin(void *data) +{ + return ((struct ep93xx_i2c_pins *)data)->scl_pin; +} + +static inline int ep93xx_sda_pin(void *data) +{ + return ((struct ep93xx_i2c_pins *)data)->sda_pin; +} + +static void ep93xx_bit_setscl(void *data, int val) +{ + gpio_line_set(ep93xx_scl_pin(data), 0); + gpio_line_config(ep93xx_scl_pin(data), + val ? GPIO_IN : GPIO_OUT); +} + +static void ep93xx_bit_setsda(void *data, int val) +{ + gpio_line_set(ep93xx_sda_pin(data), 0); + gpio_line_config(ep93xx_sda_pin(data), + val ? GPIO_IN : GPIO_OUT); +} + +static int ep93xx_bit_getscl(void *data) +{ + int scl; + + gpio_line_config(ep93xx_scl_pin(data), GPIO_IN); + scl = gpio_line_get(ep93xx_scl_pin(data)); + + return scl; +} + +static int ep93xx_bit_getsda(void *data) +{ + int sda; + + gpio_line_config(ep93xx_sda_pin(data), GPIO_IN); + sda = gpio_line_get(ep93xx_sda_pin(data)); + + return sda; +} + +struct ep93xx_i2c_data { + struct ep93xx_i2c_pins *gpio_pins; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo_data; +}; + +static int ep93xx_i2c_remove(struct platform_device *plat_dev) +{ + struct ep93xx_i2c_data *drv_data = platform_get_drvdata(plat_dev); + + platform_set_drvdata(plat_dev, NULL); + + i2c_bit_del_bus(&drv_data->adapter); + + kfree(drv_data); + + return 0; +} + +static int ep93xx_i2c_probe(struct platform_device *plat_dev) +{ + int err; + struct ep93xx_i2c_pins *gpio = plat_dev->dev.platform_data; + struct ep93xx_i2c_data *drv_data = + kzalloc(sizeof(struct ep93xx_i2c_data), GFP_KERNEL); + + if (!drv_data) + return -ENOMEM; + + drv_data->gpio_pins = gpio; + + /* + * We could make a lot of these structures static, but + * certain platforms may have multiple GPIO-based I2C + * buses for various device domains, so we need per-device + * algo_data->data. + */ + drv_data->algo_data.data = gpio; + drv_data->algo_data.setsda = ep93xx_bit_setsda; + drv_data->algo_data.setscl = ep93xx_bit_setscl; + drv_data->algo_data.getsda = ep93xx_bit_getsda; + drv_data->algo_data.getscl = ep93xx_bit_getscl; + drv_data->algo_data.udelay = 10; + drv_data->algo_data.mdelay = 10; + drv_data->algo_data.timeout = 100; + + strlcpy(drv_data->adapter.name, plat_dev->dev.driver->name, + I2C_NAME_SIZE); + drv_data->adapter.algo_data = &drv_data->algo_data; + drv_data->adapter.class = I2C_CLASS_ALL; + + drv_data->adapter.dev.parent = &plat_dev->dev; + + gpio_line_config(gpio->scl_pin, GPIO_IN); + gpio_line_config(gpio->sda_pin, GPIO_IN); + gpio_line_set(gpio->scl_pin, 0); + gpio_line_set(gpio->sda_pin, 0); + + if ((err = i2c_bit_add_bus(&drv_data->adapter) != 0)) { + printk(KERN_ERR "ERROR: Could not install %s\n", plat_dev->dev.bus_id); + + kfree(drv_data); + return err; + } + + platform_set_drvdata(plat_dev, drv_data); + + 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_DESCRIPTION("GPIO-based I2C adapter for EP93XX systems"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alessandro Zummo "); Index: linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/core.c =================================================================== --- linux-2.6.18-rc1-git9.orig/arch/arm/mach-ep93xx/core.c +++ linux-2.6.18-rc1-git9/arch/arm/mach-ep93xx/core.c @@ -457,6 +457,19 @@ static struct platform_device ep93xx_ohc }; +static struct ep93xx_i2c_pins ep93xx_i2c_gpio_pins = { + .sda_pin = EP93XX_GPIO_LINE_EEDAT, + .scl_pin = EP93XX_GPIO_LINE_EECLK, +}; + +static struct platform_device ep93xx_i2c_device = { + .name = "ep93xx-i2c", + .id = 0, + .dev.platform_data = &ep93xx_i2c_gpio_pins, + .num_resources = 0, +}; + + void __init ep93xx_init_devices(void) { unsigned int v; @@ -477,4 +490,5 @@ void __init ep93xx_init_devices(void) platform_device_register(&ep93xx_rtc_device); platform_device_register(&ep93xx_ohci_device); + platform_device_register(&ep93xx_i2c_device); } Index: linux-2.6.18-rc1-git9/include/asm-arm/arch-ep93xx/uncompress.h =================================================================== --- linux-2.6.18-rc1-git9.orig/include/asm-arm/arch-ep93xx/uncompress.h +++ linux-2.6.18-rc1-git9/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()