diff options
author | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-10-05 10:12:53 +0000 |
---|---|---|
committer | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-10-05 10:12:53 +0000 |
commit | 5c105d9f3fd086aff195d3849dcf847d6b0bd927 (patch) | |
tree | 1229a11f725bfa58aa7c57a76898553bb5f6654a /target/linux/adm8668/files | |
download | openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.tar.gz openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.zip |
branch Attitude Adjustment
git-svn-id: svn://svn.openwrt.org/openwrt/branches/attitude_adjustment@33625 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/adm8668/files')
16 files changed, 3246 insertions, 0 deletions
diff --git a/target/linux/adm8668/files/arch/mips/adm8668/Makefile b/target/linux/adm8668/files/arch/mips/adm8668/Makefile new file mode 100644 index 000000000..0b31b97d4 --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/Makefile @@ -0,0 +1,5 @@ +# +# something witty --neutronscott +# + +obj-y := irq.o pci.o prom.o platform.o serial.o proc.o net_core.o net_intr.o diff --git a/target/linux/adm8668/files/arch/mips/adm8668/Platform b/target/linux/adm8668/files/arch/mips/adm8668/Platform new file mode 100644 index 000000000..c70cd270b --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/Platform @@ -0,0 +1,6 @@ +# +# Infineon ADM8668 WildPass +# +platform-$(CONFIG_ADM8668) += adm8668/ +cflags-$(CONFIG_ADM8668) += -I$(srctree)/arch/mips/include/asm/mach-adm8668 +load-$(CONFIG_ADM8668) += 0xffffffff80002000 diff --git a/target/linux/adm8668/files/arch/mips/adm8668/irq.c b/target/linux/adm8668/files/arch/mips/adm8668/irq.c new file mode 100644 index 000000000..e048c15c8 --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/irq.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/init.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/pm.h> +#include <linux/irq.h> +#include <asm/mipsregs.h> +#include <asm/irq_cpu.h> +#include <asm/irq.h> +#include <adm8668.h> + +static void adm8668_irq_cascade(void) +{ + int i; + unsigned long intsrc; + + intsrc = ADM8668_INTC_REG(IRQ_STATUS_REG) & IRQ_MASK; + for (i = 0; intsrc; intsrc >>= 1, i++) + if (intsrc & 0x1) + do_IRQ(i); +} + +/* + * System irq dispatch + */ +void plat_irq_dispatch(void) +{ + unsigned int pending; + + pending = read_c0_cause() & read_c0_status() & ST0_IM; + + /* timer interrupt, that we renumbered */ + if (pending & STATUSF_IP7) + do_IRQ(MIPS_CPU_IRQ_BASE + 7); + if (pending & STATUSF_IP2) + adm8668_irq_cascade(); +} + +/* + * enable 8668 irq + */ +static void enable_adm8668_irq(struct irq_data *d) +{ + int irq = d->irq; + + if ((irq < 0) || (irq > NR_IRQS)) + return; + + ADM8668_INTC_REG(IRQ_ENABLE_REG) = (1 << irq); +} + + +/* + * disable 8668 irq + */ +static void disable_adm8668_irq(struct irq_data *d) +{ + int irq = d->irq; + + if ((irq < 0) || (irq > NR_IRQS)) + return; + + ADM8668_INTC_REG(IRQ_DISABLE_REG) = (1 << irq); +} + +static void ack_adm8668_irq(struct irq_data *d) +{ + int irq = d->irq; + + ADM8668_INTC_REG(IRQ_DISABLE_REG) = (1 << irq); +} + +/* + * system irq type + */ + +static struct irq_chip adm8668_irq_type = { + .name = "adm8668", + .irq_ack = ack_adm8668_irq, + .irq_mask = disable_adm8668_irq, + .irq_unmask = enable_adm8668_irq +}; + +/* + * irq init + */ +static void __init init_adm8668_irqs(void) +{ + int i; + + for (i = 0; i <= INT_LVL_MAX; i++) + irq_set_chip_and_handler(i, &adm8668_irq_type, + handle_level_irq); + + /* hw0 is where our interrupts are uh.. interrupted at. */ + set_c0_status(IE_IRQ0); +} + +/* + * system init + */ +void __init arch_init_irq(void) +{ + mips_cpu_irq_init(); + init_adm8668_irqs(); +} diff --git a/target/linux/adm8668/files/arch/mips/adm8668/net.h b/target/linux/adm8668/files/arch/mips/adm8668/net.h new file mode 100644 index 000000000..c7aef8cf6 --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/net.h @@ -0,0 +1,277 @@ +/* + * originally drivers/net/tulip/tulip.h + * Copyright 2000,2001 The Linux Kernel Team + * Written/copyright 1994-2001 by Donald Becker. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#ifndef __NET_TULIP_H__ +#define __NET_TULIP_H__ + +#include <linux/module.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/mii.h> +#include <linux/crc32.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/etherdevice.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <asm/unaligned.h> +#include <asm/uaccess.h> + +/* undefine, or define to various debugging levels (>4 == obscene levels) */ +#define TULIP_DEBUG 1 +#define VALID_INTR 0x0001a451 +#define ADM8668_WAN_IRQ 8 +#define ADM8668_LAN_IRQ 7 +#define ADM8668_WAN_MACADDR 0xb00205ac +#define ADM8668_LAN_MACADDR 0xb0020404 + +/* Offsets to the Command and Status Registers, "CSRs". All accesses + must be longword instructions and quadword aligned. */ +enum tulip_offsets { + CSR0 = 0, + CSR1 = 0x08, + CSR2 = 0x10, + CSR3 = 0x18, + CSR4 = 0x20, + CSR5 = 0x28, + CSR6 = 0x30, + CSR7 = 0x38, + CSR8 = 0x40, + CSR9 = 0x48, + CSR10 = 0x50, + CSR11 = 0x58, + CSR12 = 0x60, + CSR13 = 0x68, + CSR14 = 0x70, + CSR15 = 0x78, + CSR18 = 0x88, + CSR19 = 0x8c, + CSR20 = 0x90, + CSR27 = 0xAC, + CSR28 = 0xB0, +}; + +#define RxPollInt (RxIntr|RxNoBuf|RxDied|RxJabber) + +/* The bits in the CSR5 status registers, mostly interrupt sources. */ +enum status_bits { + TimerInt = 0x800, + SystemError = 0x2000, + TPLnkFail = 0x1000, + TPLnkPass = 0x10, + NormalIntr = 0x10000, + AbnormalIntr = 0x8000, + RxJabber = 0x200, + RxDied = 0x100, + RxNoBuf = 0x80, + RxIntr = 0x40, + TxFIFOUnderflow = 0x20, + RxErrIntr = 0x10, + TxJabber = 0x08, + TxNoBuf = 0x04, + TxDied = 0x02, + TxIntr = 0x01, +}; + +/* bit mask for CSR5 TX/RX process state */ +#define CSR5_TS 0x00700000 +#define CSR5_RS 0x000e0000 + +enum tulip_mode_bits { + TxThreshold = (1 << 22), + FullDuplex = (1 << 9), + TxOn = 0x2000, + AcceptBroadcast = 0x0100, + AcceptAllMulticast = 0x0080, + AcceptAllPhys = 0x0040, + AcceptRunt = 0x0008, + RxOn = 0x0002, + RxTx = (TxOn | RxOn), +}; + +/* The Tulip Rx and Tx buffer descriptors. */ +struct tulip_rx_desc { + __le32 status; + __le32 length; + __le32 buffer1; + __le32 buffer2; +}; + +struct tulip_tx_desc { + __le32 status; + __le32 length; + __le32 buffer1; + __le32 buffer2; /* We use only buffer 1. */ +}; + +enum desc_status_bits { + DescOwned = 0x80000000, + DescWholePkt = 0x60000000, + DescEndPkt = 0x40000000, + DescStartPkt = 0x20000000, + DescEndRing = 0x02000000, + DescUseLink = 0x01000000, + + /* + * Error summary flag is logical or of 'CRC Error', 'Collision Seen', + * 'Frame Too Long', 'Runt' and 'Descriptor Error' flags generated + * within tulip chip. + */ + RxDescErrorSummary = 0x8000, + RxDescCRCError = 0x0002, + RxDescCollisionSeen = 0x0040, + + /* + * 'Frame Too Long' flag is set if packet length including CRC exceeds + * 1518. However, a full sized VLAN tagged frame is 1522 bytes + * including CRC. + * + * The tulip chip does not block oversized frames, and if this flag is + * set on a receive descriptor it does not indicate the frame has been + * truncated. The receive descriptor also includes the actual length. + * Therefore we can safety ignore this flag and check the length + * ourselves. + */ + RxDescFrameTooLong = 0x0080, + RxDescRunt = 0x0800, + RxDescDescErr = 0x4000, + RxWholePkt = 0x00000300, + /* + * Top three bits of 14 bit frame length (status bits 27-29) should + * never be set as that would make frame over 2047 bytes. The Receive + * Watchdog flag (bit 4) may indicate the length is over 2048 and the + * length field is invalid. + */ + RxLengthOver2047 = 0x38000010 +}; + +/* Keep the ring sizes a power of two for efficiency. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ + +#define TX_RING_SIZE 32 +#define RX_RING_SIZE 128 + +/* The receiver on the DC21143 rev 65 can fail to close the last + * receive descriptor in certain circumstances (see errata) when + * using MWI. This can only occur if the receive buffer ends on + * a cache line boundary, so the "+ 4" below ensures it doesn't. + */ +#define PKT_BUF_SZ (1536 + 4) /* Size of each temporary Rx buffer. */ + +/* Ring-wrap flag in length field, use for last ring entry. + 0x01000000 means chain on buffer2 address, + 0x02000000 means use the ring start address in CSR2/3. + Note: Some work-alike chips do not function correctly in chained mode. + The ASIX chip works only in chained mode. + Thus we indicates ring mode, but always write the 'next' field for + chained mode as well. +*/ +#define DESC_RING_WRAP 0x02000000 + +struct ring_info { + struct sk_buff *skb; + dma_addr_t mapping; +}; + +struct tulip_private { + struct tulip_rx_desc *rx_ring; + struct tulip_tx_desc *tx_ring; + dma_addr_t rx_ring_dma; + dma_addr_t tx_ring_dma; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct ring_info tx_buffers[TX_RING_SIZE]; + /* The addresses of receive-in-place skbuffs. */ + struct ring_info rx_buffers[RX_RING_SIZE]; + struct napi_struct napi; + struct net_device_stats stats; + struct timer_list oom_timer; /* Out of memory timer. */ + u32 mc_filter[2]; + spinlock_t lock; + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + unsigned int csr0; /* CSR0 setting. */ + unsigned int csr6; /* Current CSR6 control settings. */ + void (*link_change) (struct net_device * dev, int csr5); + struct platform_device *pdev; + unsigned long nir; + void __iomem *base_addr; + int pad0; /* Used for 8-byte alignment */ + struct net_device *dev; +}; + + +/* interrupt.c */ +irqreturn_t tulip_interrupt(int irq, void *dev_instance); +int tulip_refill_rx(struct net_device *dev); +int tulip_poll(struct napi_struct *napi, int budget); + +/* tulip_core.c */ +extern int tulip_debug; +void oom_timer(unsigned long data); + +static inline void tulip_start_rxtx(struct tulip_private *tp) +{ + void __iomem *ioaddr = tp->base_addr; + iowrite32(tp->csr6 | RxTx, ioaddr + CSR6); + barrier(); + (void) ioread32(ioaddr + CSR6); /* mmio sync */ +} + +static inline void tulip_stop_rxtx(struct tulip_private *tp) +{ + void __iomem *ioaddr = tp->base_addr; + u32 csr6 = ioread32(ioaddr + CSR6); + + if (csr6 & RxTx) { + unsigned i=1300/10; + iowrite32(csr6 & ~RxTx, ioaddr + CSR6); + barrier(); + /* wait until in-flight frame completes. + * Max time @ 10BT: 1500*8b/10Mbps == 1200us (+ 100us margin) + * Typically expect this loop to end in < 50 us on 100BT. + */ + while (--i && (ioread32(ioaddr + CSR5) & (CSR5_TS|CSR5_RS))) + udelay(10); + + if (!i) + printk(KERN_DEBUG "fixme: tulip_stop_rxtx() failed" + " (CSR5 0x%x CSR6 0x%x)\n", + ioread32(ioaddr + CSR5), + ioread32(ioaddr + CSR6)); + } +} + +static inline void tulip_restart_rxtx(struct tulip_private *tp) +{ + tulip_stop_rxtx(tp); + udelay(5); + tulip_start_rxtx(tp); +} + +static inline void tulip_tx_timeout_complete(struct tulip_private *tp, void __iomem *ioaddr) +{ + /* Stop and restart the chip's Tx processes. */ + tulip_restart_rxtx(tp); + /* Trigger an immediate transmit demand. */ + iowrite32(0, ioaddr + CSR1); + + tp->stats.tx_errors++; +} + +#endif /* __NET_TULIP_H__ */ diff --git a/target/linux/adm8668/files/arch/mips/adm8668/net_core.c b/target/linux/adm8668/files/arch/mips/adm8668/net_core.c new file mode 100644 index 000000000..3348c224b --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/net_core.c @@ -0,0 +1,618 @@ +/* + * originally drivers/net/tulip_core.c + * Copyright 2000,2001 The Linux Kernel Team + * Written/copyright 1994-2001 by Donald Becker. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#define DRV_NAME "tulip" +#define DRV_VERSION "1.1.15-NAPI" /* Keep at least for test */ +#define DRV_RELDATE "Feb 27, 2007" + +#include "net.h" + +static char version[] __devinitdata = + "ADM8668net driver version " DRV_VERSION " (" DRV_RELDATE ")\n"; + +#define MAX_UNITS 2 + +/* + Set the bus performance register. + Typical: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 No alignment 0x00000000 unlimited 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords + Warning: many older 486 systems are broken and require setting 0x00A04800 + 8 longword cache alignment, 8 longword burst. + ToDo: Non-Intel setting could be better. +*/ + +//static int csr0 = 0x00200000 | 0x4000; +static int csr0 = 0; + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) + +MODULE_AUTHOR("Scott Nicholas <neutronscott@scottn.us>"); +MODULE_DESCRIPTION("ADM8668 new ethernet driver."); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +#ifdef TULIP_DEBUG +int tulip_debug = TULIP_DEBUG; +#else +int tulip_debug = 1; +#endif + +static void tulip_tx_timeout(struct net_device *dev); +static void tulip_init_ring(struct net_device *dev); +static void tulip_free_ring(struct net_device *dev); +static netdev_tx_t tulip_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static int tulip_open(struct net_device *dev); +static int tulip_close(struct net_device *dev); +static void tulip_up(struct net_device *dev); +static void tulip_down(struct net_device *dev); +static struct net_device_stats *tulip_get_stats(struct net_device *dev); +//static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void set_rx_mode(struct net_device *dev); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void poll_tulip(struct net_device *dev); +#endif + +static void tulip_up(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + + napi_enable(&tp->napi); + + /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ + iowrite32(0x00000001, ioaddr + CSR0); + + /* Deassert reset. + Wait the specified 50 PCI cycles after a reset by initializing + Tx and Rx queues and the address filter list. */ + iowrite32(tp->csr0, ioaddr + CSR0); + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: tulip_up(), irq==%d\n", + dev->name, dev->irq); + + iowrite32(tp->rx_ring_dma, ioaddr + CSR3); + iowrite32(tp->tx_ring_dma, ioaddr + CSR4); + tp->cur_rx = tp->cur_tx = 0; + tp->dirty_rx = tp->dirty_tx = 0; + + /* set mac address */ + iowrite32(get_unaligned_le32(dev->dev_addr), ioaddr + 0xA4); + iowrite32(get_unaligned_le16(dev->dev_addr + 4), ioaddr + 0xA8); + iowrite32(0, ioaddr + CSR27); + iowrite32(0, ioaddr + CSR28); + + tp->csr6 = 0; + + /* Enable automatic Tx underrun recovery. */ + iowrite32(ioread32(ioaddr + CSR18) | 1, ioaddr + CSR18); + tp->csr6 = 0x00040000; + + /* Start the chip's Tx to process setup frame. */ + tulip_stop_rxtx(tp); + barrier(); + udelay(5); + iowrite32(tp->csr6 | TxOn, ioaddr + CSR6); + + /* Enable interrupts by setting the interrupt mask. */ + iowrite32(VALID_INTR, ioaddr + CSR5); + iowrite32(VALID_INTR, ioaddr + CSR7); + tulip_start_rxtx(tp); + iowrite32(0, ioaddr + CSR2); /* Rx poll demand */ + + if (tulip_debug > 2) { + printk(KERN_DEBUG "%s: Done tulip_up(), CSR0 %08x, CSR5 %08x CSR6 %08x\n", + dev->name, ioread32(ioaddr + CSR0), + ioread32(ioaddr + CSR5), + ioread32(ioaddr + CSR6)); + } + + init_timer(&tp->oom_timer); + tp->oom_timer.data = (unsigned long)dev; + tp->oom_timer.function = oom_timer; +} + +static int +tulip_open(struct net_device *dev) +{ + int retval; + + tulip_init_ring (dev); + + retval = request_irq(dev->irq, tulip_interrupt, 0, dev->name, dev); + if (retval) + goto free_ring; + + tulip_up (dev); + + netif_start_queue (dev); + + return 0; + +free_ring: + tulip_free_ring (dev); + return retval; +} + + +static void tulip_tx_timeout(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + unsigned long flags; + + spin_lock_irqsave (&tp->lock, flags); + + dev_warn(&dev->dev, + "Transmit timed out, status %08x, CSR12 %08x, resetting...\n", + ioread32(ioaddr + CSR5), ioread32(ioaddr + CSR12)); + + tulip_tx_timeout_complete(tp, ioaddr); + + spin_unlock_irqrestore (&tp->lock, flags); + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_wake_queue (dev); +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void tulip_init_ring(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + int i; + + tp->nir = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + tp->rx_ring[i].status = 0x00000000; + tp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ); + tp->rx_ring[i].buffer2 = cpu_to_le32(tp->rx_ring_dma + sizeof(struct tulip_rx_desc) * (i + 1)); + tp->rx_buffers[i].skb = NULL; + tp->rx_buffers[i].mapping = 0; + } + /* Mark the last entry as wrapping the ring. */ + tp->rx_ring[i-1].length = cpu_to_le32(PKT_BUF_SZ | DESC_RING_WRAP); + tp->rx_ring[i-1].buffer2 = cpu_to_le32(tp->rx_ring_dma); + + for (i = 0; i < RX_RING_SIZE; i++) { + dma_addr_t mapping; + /* Note the receive buffer must be longword aligned. + dev_alloc_skb() provides 16 byte alignment. But do *not* + use skb_reserve() to align the IP header! */ + struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); + tp->rx_buffers[i].skb = skb; + if (skb == NULL) + break; + mapping = dma_map_single(&dev->dev, skb->data, + PKT_BUF_SZ, DMA_FROM_DEVICE); + tp->rx_buffers[i].mapping = mapping; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[i].status = cpu_to_le32(DescOwned); /* Owned by Tulip chip */ + tp->rx_ring[i].buffer1 = cpu_to_le32(mapping); + } + tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + /* The Tx buffer descriptor is filled in as needed, but we + do need to clear the ownership bit. */ + for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_buffers[i].skb = NULL; + tp->tx_buffers[i].mapping = 0; + tp->tx_ring[i].status = 0x00000000; + tp->tx_ring[i].buffer2 = cpu_to_le32(tp->tx_ring_dma + sizeof(struct tulip_tx_desc) * (i + 1)); + } + tp->tx_ring[i-1].buffer2 = cpu_to_le32(tp->tx_ring_dma); +} + +static netdev_tx_t +tulip_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + int entry; + u32 flag; + dma_addr_t mapping; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % TX_RING_SIZE; + + tp->tx_buffers[entry].skb = skb; + mapping = dma_map_single(&tp->pdev->dev, skb->data, skb->len, + DMA_TO_DEVICE); + tp->tx_buffers[entry].mapping = mapping; + tp->tx_ring[entry].buffer1 = cpu_to_le32(mapping); + + if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ + flag = 0x60000000; /* No interrupt */ + } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { + flag = 0xe0000000; /* Tx-done intr. */ + } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { + flag = 0x60000000; /* No Tx-done intr. */ + } else { /* Leave room for set_rx_mode() to fill entries. */ + flag = 0xe0000000; /* Tx-done intr. */ + netif_stop_queue(dev); + } + if (entry == TX_RING_SIZE-1) + flag = 0xe0000000 | DESC_RING_WRAP; + + tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag); + /* if we were using Transmit Automatic Polling, we would need a + * wmb() here. */ + tp->tx_ring[entry].status = cpu_to_le32(DescOwned); + wmb(); + + tp->cur_tx++; + + /* Trigger an immediate transmit demand. */ + iowrite32(0, tp->base_addr + CSR1); + + spin_unlock_irqrestore(&tp->lock, flags); + + return NETDEV_TX_OK; +} + +static void tulip_clean_tx_ring(struct tulip_private *tp) +{ + unsigned int dirty_tx; + + for (dirty_tx = tp->dirty_tx ; tp->cur_tx - dirty_tx > 0; + dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + int status = le32_to_cpu(tp->tx_ring[entry].status); + + if (status < 0) { + tp->stats.tx_errors++; /* It wasn't Txed */ + tp->tx_ring[entry].status = 0; + } + + dma_unmap_single(&tp->pdev->dev, tp->tx_buffers[entry].mapping, + tp->tx_buffers[entry].skb->len, + DMA_TO_DEVICE); + + /* Free the original skb. */ + dev_kfree_skb_irq(tp->tx_buffers[entry].skb); + tp->tx_buffers[entry].skb = NULL; + tp->tx_buffers[entry].mapping = 0; + } +} + +static void tulip_down (struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + unsigned long flags; + + napi_disable(&tp->napi); + del_timer_sync (&tp->oom_timer); + spin_lock_irqsave (&tp->lock, flags); + + /* Disable interrupts by clearing the interrupt mask. */ + iowrite32 (0x00000000, ioaddr + CSR7); + + /* Stop the Tx and Rx processes. */ + tulip_stop_rxtx(tp); + + /* prepare receive buffers */ + tulip_refill_rx(dev); + + /* release any unconsumed transmit buffers */ + tulip_clean_tx_ring(tp); + + if (ioread32 (ioaddr + CSR6) != 0xffffffff) + tp->stats.rx_missed_errors += ioread32 (ioaddr + CSR8) & 0xffff; + + spin_unlock_irqrestore (&tp->lock, flags); +} + +static void tulip_free_ring (struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + int i; + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = tp->rx_buffers[i].skb; + dma_addr_t mapping = tp->rx_buffers[i].mapping; + + tp->rx_buffers[i].skb = NULL; + tp->rx_buffers[i].mapping = 0; + + tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */ + tp->rx_ring[i].length = 0; + /* An invalid address. */ + tp->rx_ring[i].buffer1 = cpu_to_le32(0xBADF00D0); + if (skb) { + dma_unmap_single(&tp->pdev->dev, mapping, PKT_BUF_SZ, + DMA_FROM_DEVICE); + dev_kfree_skb (skb); + } + } + + for (i = 0; i < TX_RING_SIZE; i++) { + struct sk_buff *skb = tp->tx_buffers[i].skb; + + if (skb != NULL) { + dma_unmap_single(&tp->pdev->dev, + tp->tx_buffers[i].mapping, skb->len, DMA_TO_DEVICE); + dev_kfree_skb (skb); + } + tp->tx_buffers[i].skb = NULL; + tp->tx_buffers[i].mapping = 0; + } +} + +static int tulip_close (struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + + netif_stop_queue (dev); + + tulip_down (dev); + + if (tulip_debug > 1) + dev_printk(KERN_DEBUG, &dev->dev, + "Shutting down ethercard, status was %02x\n", + ioread32 (ioaddr + CSR5)); + + free_irq (dev->irq, dev); + + tulip_free_ring (dev); + + return 0; +} + +static struct net_device_stats *tulip_get_stats(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + + if (netif_running(dev)) { + unsigned long flags; + + spin_lock_irqsave (&tp->lock, flags); + + tp->stats.rx_missed_errors += ioread32(ioaddr + CSR8) & 0xffff; + + spin_unlock_irqrestore(&tp->lock, flags); + } + + return &tp->stats; +} + + +static void tulip_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, "mmio"); +} + +static const struct ethtool_ops ops = { + .get_drvinfo = tulip_get_drvinfo +}; + +static void set_rx_mode(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int csr6; + + csr6 = ioread32(ioaddr + CSR6) & ~0x00D5; + + tp->csr6 &= ~0x00D5; + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + tp->csr6 |= AcceptAllMulticast | AcceptAllPhys; + csr6 |= AcceptAllMulticast | AcceptAllPhys; + } else if ((netdev_mc_count(dev) > 1000) || + (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter well -- accept all multicasts. */ + tp->csr6 |= AcceptAllMulticast; + csr6 |= AcceptAllMulticast; + } else { + /* Some work-alikes have only a 64-entry hash filter table. */ + /* Should verify correctness on big-endian/__powerpc__ */ + struct netdev_hw_addr *ha; + if (netdev_mc_count(dev) > 64) { + /* Arbitrary non-effective limit. */ + tp->csr6 |= AcceptAllMulticast; + csr6 |= AcceptAllMulticast; + } else { + u32 mc_filter[2] = {0, 0}; /* Multicast hash filter */ + int filterbit; + netdev_for_each_mc_addr(ha, dev) { + filterbit = ether_crc_le(ETH_ALEN, ha->addr); + filterbit &= 0x3f; + mc_filter[filterbit >> 5] |= 1 << (filterbit & 31); + if (tulip_debug > 2) + dev_info(&dev->dev, + "Added filter for %pM %08x bit %d\n", + ha->addr, + ether_crc(ETH_ALEN, ha->addr), + filterbit); + } + if (mc_filter[0] == tp->mc_filter[0] && + mc_filter[1] == tp->mc_filter[1]) + ; /* No change. */ + iowrite32(mc_filter[0], ioaddr + CSR27); + iowrite32(mc_filter[1], ioaddr + CSR28); + tp->mc_filter[0] = mc_filter[0]; + tp->mc_filter[1] = mc_filter[1]; + } + } + + if (dev->irq == ADM8668_LAN_IRQ) + csr6 |= (1 << 9); /* force 100Mbps full duplex */ +// csr6 |= 1; /* pad 2 bytes. vlan? */ + + iowrite32(csr6, ioaddr + CSR6); +} + +static const struct net_device_ops tulip_netdev_ops = { + .ndo_open = tulip_open, + .ndo_start_xmit = tulip_start_xmit, + .ndo_tx_timeout = tulip_tx_timeout, + .ndo_stop = tulip_close, + .ndo_get_stats = tulip_get_stats, + .ndo_set_rx_mode = set_rx_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = poll_tulip, +#endif +}; + +static int __devinit adm8668net_probe(struct platform_device *pdev) +{ + struct tulip_private *tp; + struct net_device *dev; + struct resource *res; + void __iomem *ioaddr; + int irq; + + if (pdev->id < 0 || pdev->id >= MAX_UNITS) + return -EINVAL; + + if (!(res = platform_get_resource(pdev, IORESOURCE_IRQ, 0))) + return -ENODEV; + irq = res->start; + if (!(res = platform_get_resource(pdev, IORESOURCE_MEM, 0))) + return -ENODEV; + if (!(ioaddr = ioremap(res->start, res->end - res->start))) + return -ENODEV; + if (!(dev = alloc_etherdev(sizeof (*tp)))) + return -ENOMEM; + + /* setup net dev */ + dev->base_addr = (unsigned long)res->start; + dev->irq = irq; + SET_NETDEV_DEV(dev, &pdev->dev); + + /* tulip private struct */ + tp = netdev_priv(dev); + tp->dev = dev; + tp->base_addr = ioaddr; + tp->csr0 = csr0; + tp->pdev = pdev; + tp->rx_ring = dma_alloc_coherent(&pdev->dev, + sizeof(struct tulip_rx_desc) * RX_RING_SIZE + + sizeof(struct tulip_tx_desc) * TX_RING_SIZE, + &tp->rx_ring_dma, GFP_KERNEL); + if (!tp->rx_ring) + return -ENODEV; + tp->tx_ring = (struct tulip_tx_desc *)(tp->rx_ring + RX_RING_SIZE); + tp->tx_ring_dma = tp->rx_ring_dma + sizeof(struct tulip_rx_desc) * RX_RING_SIZE; + + spin_lock_init(&tp->lock); + + /* Stop the chip's Tx and Rx processes. */ + tulip_stop_rxtx(tp); + + /* Clear the missed-packet counter. */ + ioread32(ioaddr + CSR8); + + /* Addresses are stored in BSP area of NOR flash */ + if (irq == ADM8668_WAN_IRQ) + memcpy(dev->dev_addr, (char *)ADM8668_WAN_MACADDR, 6); + else + memcpy(dev->dev_addr, (char *)ADM8668_LAN_MACADDR, 6); + + /* The Tulip-specific entries in the device structure. */ + dev->netdev_ops = &tulip_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + netif_napi_add(dev, &tp->napi, tulip_poll, 16); + SET_ETHTOOL_OPS(dev, &ops); + + if (register_netdev(dev)) + goto err_out_free_ring; + + dev_info(&dev->dev, + "ADM8668net at MMIO %#lx %pM, IRQ %d\n", + (unsigned long)dev->base_addr, dev->dev_addr, irq); + + platform_set_drvdata(pdev, dev); + return 0; + +err_out_free_ring: + dma_free_coherent(&pdev->dev, + sizeof (struct tulip_rx_desc) * RX_RING_SIZE + + sizeof (struct tulip_tx_desc) * TX_RING_SIZE, + tp->rx_ring, tp->rx_ring_dma); + return -ENODEV; +} + +static int __devexit adm8668net_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata (pdev); + struct tulip_private *tp; + + if (!dev) + return -ENODEV; + + tp = netdev_priv(dev); + unregister_netdev(dev); + dma_free_coherent(&pdev->dev, + sizeof (struct tulip_rx_desc) * RX_RING_SIZE + + sizeof (struct tulip_tx_desc) * TX_RING_SIZE, + tp->rx_ring, tp->rx_ring_dma); + iounmap(tp->base_addr); + free_netdev(dev); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ + +static void poll_tulip (struct net_device *dev) +{ + /* disable_irq here is not very nice, but with the lockless + interrupt handler we have no other choice. */ + disable_irq(dev->irq); + tulip_interrupt(dev->irq, dev); + enable_irq(dev->irq); +} +#endif + +static struct platform_driver adm8668net_platform_driver = { + .probe = adm8668net_probe, + .remove = __devexit_p(adm8668net_remove), + .driver = { + .owner = THIS_MODULE, + .name = "adm8668_eth" + }, +}; + +static int __init adm8668net_init(void) +{ + pr_info("%s", version); + return platform_driver_register(&adm8668net_platform_driver); +} + +static void __exit adm8668net_exit(void) +{ + platform_driver_unregister(&adm8668net_platform_driver); +} + +module_init(adm8668net_init); +module_exit(adm8668net_exit); diff --git a/target/linux/adm8668/files/arch/mips/adm8668/net_intr.c b/target/linux/adm8668/files/arch/mips/adm8668/net_intr.c new file mode 100644 index 000000000..113dbaebd --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/net_intr.c @@ -0,0 +1,446 @@ +/* + * originally drivers/net/tulip/interrupt.c + * Copyright 2000,2001 The Linux Kernel Team + * Written/copyright 1994-2001 by Donald Becker. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include "net.h" + +int tulip_refill_rx(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + int entry; + int refilled = 0; + + /* Refill the Rx ring buffers. */ + for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { + entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_buffers[entry].skb == NULL) { + struct sk_buff *skb; + dma_addr_t mapping; + + skb = tp->rx_buffers[entry].skb = dev_alloc_skb(PKT_BUF_SZ); + if (skb == NULL) + break; + + mapping = dma_map_single(&dev->dev, skb->data, + PKT_BUF_SZ, DMA_FROM_DEVICE); + tp->rx_buffers[entry].mapping = mapping; + + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[entry].buffer1 = cpu_to_le32(mapping); + refilled++; + } + tp->rx_ring[entry].status = cpu_to_le32(DescOwned); + } + return refilled; +} + +void oom_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = netdev_priv(dev); + napi_schedule(&tp->napi); +} + +int tulip_poll(struct napi_struct *napi, int budget) +{ + struct tulip_private *tp = container_of(napi, struct tulip_private, napi); + struct net_device *dev = tp->dev; + int entry = tp->cur_rx % RX_RING_SIZE; + int work_done = 0; + + if (tulip_debug > 4) + printk(KERN_DEBUG " In tulip_rx(), entry %d %08x\n", + entry, tp->rx_ring[entry].status); + + do { + if (ioread32(tp->base_addr + CSR5) == 0xffffffff) { + printk(KERN_DEBUG " In tulip_poll(), hardware disappeared\n"); + break; + } + /* Acknowledge current RX interrupt sources. */ + iowrite32((RxIntr | RxNoBuf), tp->base_addr + CSR5); + + + /* If we own the next entry, it is a new packet. Send it up. */ + while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) { + s32 status = le32_to_cpu(tp->rx_ring[entry].status); + short pkt_len; + + if (tp->dirty_rx + RX_RING_SIZE == tp->cur_rx) + break; + + if (tulip_debug > 5) + printk(KERN_DEBUG "%s: In tulip_rx(), entry %d %08x\n", + dev->name, entry, status); + + if (++work_done >= budget) + goto not_done; + + /* + * Omit the four octet CRC from the length. + * (May not be considered valid until we have + * checked status for RxLengthOver2047 bits) + */ + pkt_len = ((status >> 16) & 0x7ff) - 4; + +#if 0 + csr6 = ioread32(tp->base_addr + CSR6); + if (csr6 & 0x1) + pkt_len += 2; + +#endif + /* + * Maximum pkt_len is 1518 (1514 + vlan header) + * Anything higher than this is always invalid + * regardless of RxLengthOver2047 bits + */ + + if ((status & (RxLengthOver2047 | + RxDescCRCError | + RxDescCollisionSeen | + RxDescRunt | + RxDescDescErr | + RxWholePkt)) != RxWholePkt || + pkt_len > 1518) { + if ((status & (RxLengthOver2047 | + RxWholePkt)) != RxWholePkt) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (tulip_debug > 1) + dev_warn(&dev->dev, + "Oversized Ethernet frame spanned multiple buffers, status %08x!\n", + status); + tp->stats.rx_length_errors++; + } + } else { + /* There was a fatal error. */ + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %08x\n", + dev->name, status); + tp->stats.rx_errors++; /* end of a packet.*/ + if (pkt_len > 1518 || + (status & RxDescRunt)) + tp->stats.rx_length_errors++; + + if (status & 0x0004) tp->stats.rx_frame_errors++; + if (status & 0x0002) tp->stats.rx_crc_errors++; + if (status & 0x0001) tp->stats.rx_fifo_errors++; + } + } else { + struct sk_buff *skb = tp->rx_buffers[entry].skb; + char *temp = skb_put(skb, pkt_len); + +#if 0 + if (csr6 & 1) + skb_pull(skb, 2); +#endif +#ifndef final_version + if (tp->rx_buffers[entry].mapping != + le32_to_cpu(tp->rx_ring[entry].buffer1)) { + dev_err(&dev->dev, + "Internal fault: The skbuff addresses do not match in tulip_rx: %08x vs. %08llx %p / %p\n", + le32_to_cpu(tp->rx_ring[entry].buffer1), + (unsigned long long)tp->rx_buffers[entry].mapping, + skb->head, temp); + } +#endif + + tp->rx_buffers[entry].skb = NULL; + tp->rx_buffers[entry].mapping = 0; + skb->protocol = eth_type_trans(skb, dev); + + netif_receive_skb(skb); + + tp->stats.rx_packets++; + tp->stats.rx_bytes += pkt_len; + } + entry = (++tp->cur_rx) % RX_RING_SIZE; + if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/4) + tulip_refill_rx(dev); + + } + + /* New ack strategy... irq does not ack Rx any longer + hopefully this helps */ + + /* Really bad things can happen here... If new packet arrives + * and an irq arrives (tx or just due to occasionally unset + * mask), it will be acked by irq handler, but new thread + * is not scheduled. It is major hole in design. + * No idea how to fix this if "playing with fire" will fail + * tomorrow (night 011029). If it will not fail, we won + * finally: amount of IO did not increase at all. */ + } while ((ioread32(tp->base_addr + CSR5) & RxIntr)); + + tulip_refill_rx(dev); + + /* If RX ring is not full we are out of memory. */ + if (tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) + goto oom; + + /* Remove us from polling list and enable RX intr. */ + napi_complete(napi); + iowrite32(VALID_INTR, tp->base_addr+CSR7); + + /* The last op happens after poll completion. Which means the following: + * 1. it can race with disabling irqs in irq handler + * 2. it can race with dise/enabling irqs in other poll threads + * 3. if an irq raised after beginning loop, it will be immediately + * triggered here. + * + * Summarizing: the logic results in some redundant irqs both + * due to races in masking and due to too late acking of already + * processed irqs. But it must not result in losing events. + */ + + return work_done; + + not_done: + if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/2 || + tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) + tulip_refill_rx(dev); + + if (tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) + goto oom; + + return work_done; + + oom: /* Executed with RX ints disabled */ + + /* Start timer, stop polling, but do not enable rx interrupts. */ + mod_timer(&tp->oom_timer, jiffies+1); + + /* Think: timer_pending() was an explicit signature of bug. + * Timer can be pending now but fired and completed + * before we did napi_complete(). See? We would lose it. */ + + /* remove ourselves from the polling list */ + napi_complete(napi); + + return work_done; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +irqreturn_t tulip_interrupt(int irq, void *dev_instance) +{ + struct net_device *dev = (struct net_device *)dev_instance; + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int csr5; + int missed; + int rx = 0; + int tx = 0; + int oi = 0; + int maxrx = RX_RING_SIZE; + int maxtx = TX_RING_SIZE; + int maxoi = TX_RING_SIZE; + int rxd = 0; + unsigned int work_count = 25; + unsigned int handled = 0; + + /* Let's see whether the interrupt really is for us */ + csr5 = ioread32(ioaddr + CSR5); + + if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) + return IRQ_RETVAL(handled); + + tp->nir++; + + do { + + if (!rxd && (csr5 & (RxIntr | RxNoBuf))) { + rxd++; + /* Mask RX intrs and add the device to poll list. */ + iowrite32(VALID_INTR&~RxPollInt, ioaddr + CSR7); + napi_schedule(&tp->napi); + + if (!(csr5&~(AbnormalIntr|NormalIntr|RxPollInt|TPLnkPass))) + break; + } + + /* Acknowledge the interrupt sources we handle here ASAP + the poll function does Rx and RxNoBuf acking */ + + iowrite32(csr5 & 0x0001ff3f, ioaddr + CSR5); + + if (tulip_debug > 4) + printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x\n", + dev->name, csr5, ioread32(ioaddr + CSR5)); + + + if (csr5 & (TxNoBuf | TxDied | TxIntr | TimerInt)) { + unsigned int dirty_tx; + + spin_lock(&tp->lock); + + for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; + dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + int status = le32_to_cpu(tp->tx_ring[entry].status); + + if (status < 0) + break; /* It still has not been Txed */ + + if (status & 0x8000) { + /* There was an major error, log it. */ +#ifndef final_version + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %08x\n", + dev->name, status); +#endif + tp->stats.tx_errors++; + if (status & 0x4104) tp->stats.tx_aborted_errors++; + if (status & 0x0C00) tp->stats.tx_carrier_errors++; + if (status & 0x0200) tp->stats.tx_window_errors++; + if (status & 0x0002) tp->stats.tx_fifo_errors++; + if (status & 0x0080) tp->stats.tx_heartbeat_errors++; + } else { + tp->stats.tx_bytes += + tp->tx_buffers[entry].skb->len; + tp->stats.collisions += (status >> 3) & 15; + tp->stats.tx_packets++; + } + + dma_unmap_single(&tp->pdev->dev, tp->tx_buffers[entry].mapping, + tp->tx_buffers[entry].skb->len, DMA_TO_DEVICE); + /* Free the original skb. */ + dev_kfree_skb_irq(tp->tx_buffers[entry].skb); + tp->tx_buffers[entry].skb = NULL; + tp->tx_buffers[entry].mapping = 0; + tx++; + } + +#ifndef final_version + if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { + dev_err(&dev->dev, + "Out-of-sync dirty pointer, %d vs. %d\n", + dirty_tx, tp->cur_tx); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) + netif_wake_queue(dev); + + tp->dirty_tx = dirty_tx; + if (csr5 & TxDied) { + if (tulip_debug > 2) + dev_warn(&dev->dev, + "The transmitter stopped. CSR5 is %x, CSR6 %x, new CSR6 %x\n", + csr5, ioread32(ioaddr + CSR6), + tp->csr6); + tulip_restart_rxtx(tp); + } + spin_unlock(&tp->lock); + } + + /* Log errors. */ + if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 == 0xffffffff) + break; + if (csr5 & TxJabber) tp->stats.tx_errors++; + if (csr5 & TxFIFOUnderflow) { + if ((tp->csr6 & 0xC000) != 0xC000) + tp->csr6 += 0x4000; /* Bump up the Tx threshold */ + else + tp->csr6 |= 0x00200000; /* Store-n-forward. */ + /* Restart the transmit process. */ + tulip_restart_rxtx(tp); + iowrite32(0, ioaddr + CSR1); + } + if (csr5 & (RxDied | RxNoBuf)) { + iowrite32(tp->mc_filter[0], ioaddr + CSR27); + iowrite32(tp->mc_filter[1], ioaddr + CSR28); + } + if (csr5 & RxDied) { /* Missed a Rx frame. */ + tp->stats.rx_missed_errors += ioread32(ioaddr + CSR8) & 0xffff; + tp->stats.rx_errors++; + tulip_start_rxtx(tp); + } + /* + * NB: t21142_lnk_change() does a del_timer_sync(), so be careful if this + * call is ever done under the spinlock + */ + if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { + if (tp->link_change) + (tp->link_change)(dev, csr5); + } + if (csr5 & SystemError) { + int error = (csr5 >> 23) & 7; + /* oops, we hit a PCI error. The code produced corresponds + * to the reason: + * 0 - parity error + * 1 - master abort + * 2 - target abort + * Note that on parity error, we should do a software reset + * of the chip to get it back into a sane state (according + * to the 21142/3 docs that is). + * -- rmk + */ + dev_err(&dev->dev, + "(%lu) System Error occurred (%d)\n", + tp->nir, error); + } + /* Clear all error sources, included undocumented ones! */ + iowrite32(0x0800f7ba, ioaddr + CSR5); + oi++; + } + if (csr5 & TimerInt) { + + if (tulip_debug > 2) + dev_err(&dev->dev, + "Re-enabling interrupts, %08x\n", + csr5); + iowrite32(VALID_INTR, ioaddr + CSR7); + oi++; + } + if (tx > maxtx || rx > maxrx || oi > maxoi) { + if (tulip_debug > 1) + dev_warn(&dev->dev, "Too much work during an interrupt, csr5=0x%08x. (%lu) (%d,%d,%d)\n", + csr5, tp->nir, tx, rx, oi); + + /* Acknowledge all interrupt sources. */ + iowrite32(0x8001ffff, ioaddr + CSR5); + /* Mask all interrupting sources, set timer to + re-enable. */ + iowrite32(((~csr5) & 0x0001ebef) | AbnormalIntr | TimerInt, ioaddr + CSR7); + iowrite32(0x0012, ioaddr + CSR11); + break; + } + + work_count--; + if (work_count == 0) + break; + + csr5 = ioread32(ioaddr + CSR5); + + if (rxd) + csr5 &= ~RxPollInt; + } while ((csr5 & (TxNoBuf | + TxDied | + TxIntr | + TimerInt | + /* Abnormal intr. */ + RxDied | + TxFIFOUnderflow | + TxJabber | + TPLnkFail | + SystemError )) != 0); + + if ((missed = ioread32(ioaddr + CSR8) & 0x1ffff)) { + tp->stats.rx_dropped += missed & 0x10000 ? 0x10000 : missed; + } + + if (tulip_debug > 4) + printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#04x\n", + dev->name, ioread32(ioaddr + CSR5)); + + return IRQ_HANDLED; +} diff --git a/target/linux/adm8668/files/arch/mips/adm8668/pci.c b/target/linux/adm8668/files/arch/mips/adm8668/pci.c new file mode 100644 index 000000000..2bf119297 --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/pci.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/types.h> +#include <asm/byteorder.h> +#include <asm/pci.h> +#include <adm8668.h> + +volatile u32* pci_config_address_reg = (volatile u32*)KSEG1ADDR(PCICFG_BASE); +volatile u32* pci_config_data_reg = (volatile u32*)KSEG1ADDR(PCIDAT_BASE); + +#define PCI_ENABLE 0x80000000 +#define ADMPCI_IO_BASE 0x12600000 +#define ADMPCI_IO_SIZE 0x1fffff +#define ADMPCI_MEM_BASE 0x16000000 +#define ADMPCI_MEM_SIZE 0x7ffffff +#define PCI_CMM_IOACC_EN 0x1 +#define PCI_CMM_MEMACC_EN 0x2 +#define PCI_CMM_MASTER_EN 0x4 +#define PCI_CMM_DEF (PCI_CMM_IOACC_EN | PCI_CMM_MEMACC_EN | PCI_CMM_MASTER_EN) + +#define PCI_DEF_CACHE_LINE_SZ 0 +#define PCI_DEF_LATENCY_TIMER 0x20 +#define PCI_DEF_CACHE_LATENCY ((PCI_DEF_LATENCY_TIMER << 8) | PCI_DEF_CACHE_LINE_SZ) + + +#define cfgaddr(bus, devfn, where) ( \ + (bus ? ((bus->number & 0xff) << 0x10) : 0) | \ + ((devfn & 0xff) << 0x08) | \ + (where & 0xfc)) | PCI_ENABLE + +/* assumed little endian */ +static int adm8668_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + switch (size) + { + case 1: + *pci_config_address_reg = cfgaddr(bus, devfn, where); + *val = (le32_to_cpu(*pci_config_data_reg) >> ((where&3)<<3)) & 0xff; + break; + case 2: + if (where & 1) + return PCIBIOS_BAD_REGISTER_NUMBER; + *pci_config_address_reg = cfgaddr(bus, devfn, where); + *val = (le32_to_cpu(*pci_config_data_reg) >> ((where&3)<<3)) & 0xffff; + break; + case 4: + if (where & 3) + return PCIBIOS_BAD_REGISTER_NUMBER; + *pci_config_address_reg = cfgaddr(bus, devfn, where); + *val = le32_to_cpu(*pci_config_data_reg); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int adm8668_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + switch (size) + { + case 1: + *pci_config_address_reg = cfgaddr(bus, devfn, where); + *(volatile u8 *)(((int)pci_config_data_reg) + (where & 3)) = val; + break; + case 2: + if (where & 1) + return PCIBIOS_BAD_REGISTER_NUMBER; + *pci_config_address_reg = cfgaddr(bus, devfn, where); + *(volatile u16 *)(((int)pci_config_data_reg) + (where & 2)) = val; + break; + case 4: + if (where & 3) + return PCIBIOS_BAD_REGISTER_NUMBER; + *pci_config_address_reg = cfgaddr(bus, devfn, where); + *pci_config_data_reg = (val); + } + + return PCIBIOS_SUCCESSFUL; +} + + +struct pci_ops adm8668_pci_ops = { + .read = adm8668_read_config, + .write = adm8668_write_config +}; + + +struct resource pciioport_resource = { + .name = "adm8668_pci", + .start = ADMPCI_IO_BASE, + .end = ADMPCI_IO_BASE + ADMPCI_IO_SIZE, + .flags = IORESOURCE_IO +}; + + +struct resource pciiomem_resource = { + .name = "adm8668_pci", + .start = ADMPCI_MEM_BASE, + .end = ADMPCI_MEM_BASE + ADMPCI_MEM_SIZE, + .flags = IORESOURCE_MEM +}; + +#ifdef CONFIG_ADM8668_DISABLE_PCI +struct pci_controller mips_pci_channels[] = { + { NULL, NULL, NULL , NULL , NULL} +}; +#else +struct pci_controller mips_pci_channels = { + .pci_ops = &adm8668_pci_ops, + .io_resource = &pciioport_resource, + .mem_resource = &pciiomem_resource, +}; +#endif + +int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + switch (slot) + { + case 1: + return 14; + case 2: + return 13; + case 3: + return 12; + default: + return dev->irq; + } +} + +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +static int __init adm8668_pci_init(void) +{ + void __iomem *io_map_base; + + printk("adm8668_pci_init()\n"); + + /* what's an io port? this is MIPS... *shrug* */ + ioport_resource.start = ADMPCI_IO_BASE; + ioport_resource.end = ADMPCI_IO_BASE + ADMPCI_IO_SIZE; + + io_map_base = ioremap(ADMPCI_IO_BASE, ADMPCI_IO_SIZE); + if (!io_map_base) + printk("io_map_base didn't work.\n"); + mips_pci_channels.io_map_base = (unsigned long)io_map_base; + register_pci_controller(&mips_pci_channels); + + /* this needed? linksys' gpl 2.4 did it... */ + adm8668_write_config(NULL, 0, PCI_CACHE_LINE_SIZE, 2, 0); + adm8668_write_config(NULL, 0, PCI_BASE_ADDRESS_0, 4, 0); + adm8668_write_config(NULL, 0, PCI_BASE_ADDRESS_1, 4, 0); + adm8668_write_config(NULL, 0, PCI_COMMAND, 4, PCI_CMM_DEF); + + return 0; +} + +arch_initcall(adm8668_pci_init); diff --git a/target/linux/adm8668/files/arch/mips/adm8668/platform.c b/target/linux/adm8668/files/arch/mips/adm8668/platform.c new file mode 100644 index 000000000..803af09aa --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/platform.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/mtd/physmap.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <asm/reboot.h> +#include <asm/time.h> +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <adm8668.h> + +static struct resource uart_resources[] = { + { + .start = ADM8668_UART0_BASE, + .end = ADM8668_UART0_BASE + 0xF, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_LVL_UART0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device adm8668_uart_device = { + .name = "adm8668_uart", + .id = 0, + .resource = uart_resources, + .num_resources = ARRAY_SIZE(uart_resources), +}; + +static struct resource eth0_resources[] = { + { + .start = ADM8668_LAN_BASE, + .end = ADM8668_LAN_BASE + 256, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_LVL_LAN, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device adm8668_eth0_device = { + .name = "adm8668_eth", + .id = 0, + .resource = eth0_resources, + .num_resources = ARRAY_SIZE(eth0_resources), +}; + +static struct resource eth1_resources[] = { + { + .start = ADM8668_WAN_BASE, + .end = ADM8668_WAN_BASE + 256, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_LVL_WAN, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device adm8668_eth1_device = { + .name = "adm8668_eth", + .id = 1, + .resource = eth1_resources, + .num_resources = ARRAY_SIZE(eth1_resources), +}; + +static void adm8668_restart(char *cmd) +{ + int i; + + /* stop eth0 and eth1 */ + ADM8668_LAN_REG(NETCSR6) = (1 << 13) | (1 << 1); + ADM8668_LAN_REG(NETCSR7) = 0; + ADM8668_WAN_REG(NETCSR6) = (1 << 13) | (1 << 1); + ADM8668_WAN_REG(NETCSR7) = 0; + + /* reset PHY */ + ADM8668_WAN_REG(NETCSR37) = 0x20; + for (i = 0; i < 10000; i++) + ; + ADM8668_WAN_REG(NETCSR37) = 0; + for (i = 0; i < 10000; i++) + ; + + *(volatile unsigned int *)0xB1600000 = 1; /* reset eth0 mac */ + *(volatile unsigned int *)0xB1A00000 = 1; /* reset eth1 mac */ + *(volatile unsigned int *)0xB1800000 = 1; /* reset wlan0 mac */ + + /* the real deal */ + for (i = 0; i < 1000; i++) + ; + ADM8668_CONFIG_REG(ADM8668_CR1) = 1; +} + +int __devinit adm8668_devs_register(void) +{ + _machine_restart = adm8668_restart; + platform_device_register(&adm8668_uart_device); + platform_device_register(&adm8668_eth0_device); + platform_device_register(&adm8668_eth1_device); + + return 0; +} + +void __init plat_time_init(void) +{ + int adj = (ADM8668_CONFIG_REG(ADM8668_CR3) >> 11) & 0xf; + + /* adjustable clock selection + CR3 bit 14~11, 0000 -> 175MHz, 0001 -> 180MHz, etc... */ + + mips_hpt_frequency = (SYS_CLOCK + adj * 5000000) / 2; + printk("ADM8668 CPU clock: %d MHz\n", 2*mips_hpt_frequency / 1000000); +} + +void __init plat_mem_setup(void) +{ + /* prom_init seemed like easier place for this. it's tooo simple */ +} + +const char *get_system_type(void) +{ + unsigned long chipid = ADM8668_CONFIG_REG(ADM8668_CR0); + int adj = (ADM8668_CONFIG_REG(ADM8668_CR3) >> 11) & 0xf; + int product, revision, mhz; + static char ret[32]; + + product = chipid >> 16; + revision = chipid & 0xffff; + mhz = (SYS_CLOCK/1000000) + (adj * 5); + + /* i getting fancy :\ */ + snprintf(ret, sizeof(ret), "ADM%xr%x %dMHz", product, revision, mhz); + + return ret; +} + +arch_initcall(adm8668_devs_register); diff --git a/target/linux/adm8668/files/arch/mips/adm8668/proc.c b/target/linux/adm8668/files/arch/mips/adm8668/proc.c new file mode 100644 index 000000000..77dbb1faf --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/proc.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/proc_fs.h> +#include <asm/uaccess.h> +#include <adm8668.h> + +int adm8668_sesled_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) +{ + char buf[8]; + int num; + + num = (count < 8) ? count : 8; + + if (copy_from_user(buf, buffer, num)) + { + printk("copy_from_user failed"); + return -EFAULT; + } + num = simple_strtoul(buf, NULL, 16); + switch (num) + { + case 0: + GPIO_SET_LOW(0); + CRGPIO_SET_LOW(2); + break; + case 1: + GPIO_SET_LOW(0); + CRGPIO_SET_HI(2); + break; + case 2: + GPIO_SET_HI(0); + CRGPIO_SET_HI(2); + break; + default: + break; + } + + return count; +} + +int adm8668_sesled_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data) +{ + int len = 0; + int led_state = 0; + + led_state = (ADM8668_CONFIG_REG(CRGPIO_REG) & 0x100) ? 1 : 0; + led_state += (ADM8668_WLAN_REG(GPIO_REG) & 0x40) ? 2 : 0; + len += sprintf(buf+len, "%s\n", + (led_state&1) ? + ((led_state&2) ? "ORANGE" : "GREEN") : "OFF"); + + return len; +} + +int adm8668_button_read_proc(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int len = 0; + int read_once = ADM8668_CONFIG_REG(CRGPIO_REG); + int button_flip = (read_once >> 20) & 0x3; + int button_state = read_once & 0x3; + + len += sprintf(buf+len, "SES: %s %s\nRESET: %s %s\n", + (button_state&2) ? "UP" : "DOWN", + (button_flip&2) ? "FLIP" : "", + (button_state&1) ? "UP" : "DOWN", + (button_flip&1) ? "FLIP" : ""); + + return len; +} + +int __init adm8668_init_proc(void) +{ + struct proc_dir_entry *adm8668_proc_dir = NULL; + struct proc_dir_entry *sesled = NULL; + int __maybe_unused bogus; + + /* these are known to be lights. rest are input...? */ + ADM8668_CONFIG_REG(CRGPIO_REG) = GPIO2_OUTPUT_ENABLE; + ADM8668_WLAN_REG(GPIO_REG) = GPIO0_OUTPUT_ENABLE; + + /* inital read off of the flipper switcher on the button thingie */ + bogus = ADM8668_CONFIG_REG(CRGPIO_REG); + + adm8668_proc_dir = proc_mkdir("adm8668", 0); + if (adm8668_proc_dir == NULL) { + printk(KERN_ERR "ADM8668 proc: unable to create proc dir.\n"); + return 0; + } + create_proc_read_entry("buttons", 0444, adm8668_proc_dir, + adm8668_button_read_proc, NULL); + sesled = create_proc_entry("sesled", S_IRUGO|S_IWUGO, adm8668_proc_dir); + if (sesled) { + sesled->read_proc = adm8668_sesled_read_proc; + sesled->write_proc = adm8668_sesled_write_proc; + } + + return 0; +} + +module_init(adm8668_init_proc); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Scott Nicholas <neutronscott@scottn.us>"); +MODULE_DESCRIPTION("ADM8668 ghetto button driver"); diff --git a/target/linux/adm8668/files/arch/mips/adm8668/prom.c b/target/linux/adm8668/files/arch/mips/adm8668/prom.c new file mode 100644 index 000000000..7187f2982 --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/prom.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * + * based on work of rb532 prom.c + * Copyright (C) 2003, Peter Sadik <peter.sadik@idt.com> + * Copyright (C) 2005-2006, P.Christeas <p_christ@hol.gr> + * Copyright (C) 2007, Gabor Juhos <juhosg@openwrt.org> + * Felix Fietkau <nbd@openwrt.org> + * Florian Fainelli <florian@openwrt.org> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/console.h> +#include <linux/string.h> +#include <linux/serial_core.h> +#include <asm/bootinfo.h> +#include <adm8668.h> +#include "u-boot.h" + +register volatile struct global_data *gd asm ("k0"); + +#ifdef CONFIG_SERIAL_ADM8668_CONSOLE +static inline unsigned int adm_uart_readl(unsigned int offset) +{ + return (*(volatile unsigned int *)(0xbe400000 + offset)); +} + +static inline void adm_uart_writel(unsigned int value, unsigned int offset) +{ + (*((volatile unsigned int *)(0xbe400000 + offset))) = value; +} + +static void prom_putchar(char c) +{ + adm_uart_writel(c, UART_DR_REG); + while ((adm_uart_readl(UART_FR_REG) & UART_TX_FIFO_FULL) != 0) + ; +} + +static void __init +early_console_write(struct console *con, const char *s, unsigned n) +{ + while (n-- && *s) { + if (*s == '\n') + prom_putchar('\r'); + prom_putchar(*s); + s++; + } +} + +static struct console early_console __initdata = { + .name = "early", + .write = early_console_write, + .flags = CON_BOOT, + .index = -1 +}; + +#endif + +void __init prom_free_prom_memory(void) +{ + /* No prom memory to free */ +} + +static inline int match_tag(char *arg, const char *tag) +{ + return strncmp(arg, tag, strlen(tag)) == 0; +} + +static inline unsigned long tag2ul(char *arg, const char *tag) +{ + char *num; + + num = arg + strlen(tag); + return simple_strtoul(num, 0, 10); +} + +void __init prom_setup_cmdline(void) +{ + char *cp; + int prom_argc; + char **prom_argv; + int i; + + prom_argc = fw_arg0; + prom_argv = (char **)KSEG0ADDR(fw_arg1); + + cp = &(arcs_cmdline[0]); + for (i = 1; i < prom_argc; i++) { + prom_argv[i] = (char *)KSEG0ADDR(prom_argv[i]); + + /* default bootargs has "console=/dev/ttyS0" yet console won't + * show up at all if you include the '/dev/' nowadays ... */ + if (match_tag(prom_argv[i], "console=/dev/")) { + char *ptr = prom_argv[i] + strlen("console=/dev/"); + + strcpy(cp, "console="); + cp += strlen("console="); + strcpy(cp, ptr); + cp += strlen(ptr); + *cp++ = ' '; + continue; + } + strcpy(cp, prom_argv[i]); + cp += strlen(prom_argv[i]); + *cp++ = ' '; + } + if (prom_argc > 1) + --cp; /* trailing space */ + + *cp = '\0'; +} + +void __init prom_init(void) +{ + bd_t *bd = gd->bd; + int memsize; + +#ifdef CONFIG_SERIAL_ADM8668_CONSOLE + register_console(&early_console); +#endif + + memsize = bd->bi_memsize; + printk("Board info:\n"); + printk(" RAM size: %d MB\n", (int)memsize/(1024*1024)); + printk(" NOR start: %#lx\n", bd->bi_flashstart); + printk(" NOR size: %#lx\n", bd->bi_flashsize); + + prom_setup_cmdline(); + add_memory_region(0, memsize, BOOT_MEM_RAM); +} diff --git a/target/linux/adm8668/files/arch/mips/adm8668/serial.c b/target/linux/adm8668/files/arch/mips/adm8668/serial.c new file mode 100644 index 000000000..7d684b810 --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/serial.c @@ -0,0 +1,638 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * ADM8668 serial driver, totally ripped the source from BCM63xx and changed + * all the registers to fit our hardware, and removed all the features that + * I didn't know because our GPL'd serial driver was way lame. + * + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * Derived directly from bcm63xx_uart + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + * + */ + +#if defined(CONFIG_SERIAL_ADM8668_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/console.h> +#include <linux/clk.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/sysrq.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <adm8668.h> + +#define ADM8668_NR_UARTS 1 + +static struct uart_port ports[ADM8668_NR_UARTS]; + +/* + * handy uart register accessor + */ +static inline unsigned int adm_uart_readl(struct uart_port *port, + unsigned int offset) +{ + return (*(volatile unsigned int *)(port->membase + offset)); +} + +static inline void adm_uart_writel(struct uart_port *port, + unsigned int value, unsigned int offset) +{ + (*((volatile unsigned int *)(port->membase + offset))) = value; +} + +/* + * serial core request to check if uart tx fifo is empty + */ +static unsigned int adm_uart_tx_empty(struct uart_port *port) +{ + /* we always wait for completion, no buffer is made... */ + return 1; +} + +/* + * serial core request to set RTS and DTR pin state and loopback mode + */ +static void adm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +/* + * serial core request to return RI, CTS, DCD and DSR pin state + */ +static unsigned int adm_uart_get_mctrl(struct uart_port *port) +{ + return 0; +} + +/* + * serial core request to disable tx ASAP (used for flow control) + */ +static void adm_uart_stop_tx(struct uart_port *port) +{ + unsigned int val; + + val = adm_uart_readl(port, UART_CR_REG); + val &= ~(UART_TX_INT_EN); + adm_uart_writel(port, val, UART_CR_REG); +} + +/* + * serial core request to (re)enable tx + */ +static void adm_uart_start_tx(struct uart_port *port) +{ + unsigned int val; + + val = adm_uart_readl(port, UART_CR_REG); + val |= UART_TX_INT_EN; + adm_uart_writel(port, val, UART_CR_REG); +} + +/* + * serial core request to stop rx, called before port shutdown + */ +static void adm_uart_stop_rx(struct uart_port *port) +{ + unsigned int val; + + val = adm_uart_readl(port, UART_CR_REG); + val &= ~UART_RX_INT_EN; + adm_uart_writel(port, val, UART_CR_REG); +} + +/* + * serial core request to enable modem status interrupt reporting + */ +static void adm_uart_enable_ms(struct uart_port *port) +{ +} + +/* + * serial core request to start/stop emitting break char + */ +static void adm_uart_break_ctl(struct uart_port *port, int ctl) +{ +} + +/* + * return port type in string format + */ +static const char *adm_uart_type(struct uart_port *port) +{ + return (port->type == PORT_ADM8668) ? "adm8668_uart" : NULL; +} + +/* + * read all chars in rx fifo and send them to core + */ +static void adm_uart_do_rx(struct uart_port *port) +{ + struct tty_struct *tty; + unsigned int max_count; + + /* limit number of char read in interrupt, should not be + * higher than fifo size anyway since we're much faster than + * serial port */ + max_count = 32; + tty = port->state->port.tty; + do { + unsigned int iestat, c, cstat; + char flag; + + /* get overrun/fifo empty information from ier + * register */ + iestat = adm_uart_readl(port, UART_FR_REG); + if (iestat & UART_RX_FIFO_EMPTY) + break; + + /* recieve status */ + cstat = adm_uart_readl(port, UART_RSR_REG); + /* clear errors */ + adm_uart_writel(port, cstat, UART_RSR_REG); + + c = adm_uart_readl(port, UART_DR_REG); + port->icount.rx++; + flag = TTY_NORMAL; + + if (unlikely((cstat & UART_RX_STATUS_MASK))) { + /* do stats first */ + if (cstat & UART_BREAK_ERR) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } + + if (cstat & UART_PARITY_ERR) + port->icount.parity++; + if (cstat & UART_FRAMING_ERR) + port->icount.frame++; + if (cstat & UART_OVERRUN_ERR) { + port->icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + /* update flag wrt read_status_mask */ + cstat &= port->read_status_mask; + if (cstat & UART_BREAK_ERR) + flag = TTY_BREAK; + if (cstat & UART_FRAMING_ERR) + flag = TTY_FRAME; + if (cstat & UART_PARITY_ERR) + flag = TTY_PARITY; + } + + if (uart_handle_sysrq_char(port, c)) + continue; + + /* fixthis */ + if ((cstat & port->ignore_status_mask) == 0) + tty_insert_flip_char(tty, c, flag); + } while (max_count--); + + tty_flip_buffer_push(tty); +} + +/* + * fill tx fifo with chars to send, stop when fifo is about to be full + * or when all chars have been sent. + */ +static void adm_uart_do_tx(struct uart_port *port) +{ + struct circ_buf *xmit; + + if (port->x_char) { + adm_uart_writel(port, port->x_char, UART_DR_REG); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_tx_stopped(port)) + adm_uart_stop_tx(port); + + xmit = &port->state->xmit; + + if (uart_circ_empty(xmit)) + goto txq_empty; + do + { + while ((adm_uart_readl(port, UART_FR_REG) & UART_TX_FIFO_FULL) != 0) + ; + adm_uart_writel(port, xmit->buf[xmit->tail], UART_DR_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (1); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + goto txq_empty; + + return; + +txq_empty: + adm_uart_stop_tx(port); +} + +/* + * process uart interrupt + */ +static irqreturn_t adm_uart_interrupt(int irq, void *dev_id) +{ + struct uart_port *port; + unsigned int irqstat; + + port = dev_id; + spin_lock(&port->lock); + + irqstat = adm_uart_readl(port, UART_IIR_REG); + + if (irqstat & (UART_RX_INT|UART_RX_TIMEOUT_INT)) { + adm_uart_do_rx(port); + } + + if (irqstat & UART_TX_INT) { + adm_uart_do_tx(port); + } + spin_unlock(&port->lock); + return IRQ_HANDLED; +} + +/* + * enable rx & tx operation on uart + */ +static void adm_uart_enable(struct uart_port *port) +{ + unsigned int val; + + val = adm_uart_readl(port, UART_CR_REG); + // BREAK_INT too + val |= (UART_RX_INT_EN | UART_RX_TIMEOUT_INT_EN); + adm_uart_writel(port, val, UART_CR_REG); +} + +/* + * disable rx & tx operation on uart + */ +static void adm_uart_disable(struct uart_port *port) +{ + unsigned int val; + + val = adm_uart_readl(port, UART_CR_REG); + val &= ~(UART_TX_INT_EN | UART_RX_INT_EN | UART_RX_TIMEOUT_INT_EN); + adm_uart_writel(port, val, UART_CR_REG); +} + +/* + * clear all unread data in rx fifo and unsent data in tx fifo + */ +static void adm_uart_flush(struct uart_port *port) +{ + /* read any pending char to make sure all irq status are + * cleared */ + (void)adm_uart_readl(port, UART_DR_REG); +} + +/* + * serial core request to initialize uart and start rx operation + */ +static int adm_uart_startup(struct uart_port *port) +{ + int ret; + + /* clear any pending external input interrupt */ + (void)adm_uart_readl(port, UART_IIR_REG); + + /* register irq and enable rx interrupts */ + ret = request_irq(port->irq, adm_uart_interrupt, 0, + adm_uart_type(port), port); + if (ret) + return ret; + + adm_uart_enable(port); + + return 0; +} + +/* + * serial core request to flush & disable uart + */ +static void adm_uart_shutdown(struct uart_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); +// adm_uart_writel(port, 0, UART_CR_REG); + spin_unlock_irqrestore(&port->lock, flags); + + adm_uart_disable(port); + adm_uart_flush(port); + free_irq(port->irq, port); +} + +/* + * serial core request to change current uart setting + */ +static void adm_uart_set_termios(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + port->ignore_status_mask = 0; + uart_update_timeout(port, new->c_cflag, 115200); +} + +/* + * serial core request to claim uart iomem + */ +static int adm_uart_request_port(struct uart_port *port) +{ + unsigned int size = 0xf; + if (!request_mem_region(port->mapbase, size, "adm8668")) { + dev_err(port->dev, "Memory region busy\n"); + return -EBUSY; + } + + port->membase = ioremap(port->mapbase, size); + if (!port->membase) { + dev_err(port->dev, "Unable to map registers\n"); + release_mem_region(port->mapbase, size); + return -EBUSY; + } + return 0; +} + +/* + * serial core request to release uart iomem + */ +static void adm_uart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, 0xf); + iounmap(port->membase); +} + +/* + * serial core request to do any port required autoconfiguration + */ +static void adm_uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + if (adm_uart_request_port(port)) + return; + port->type = PORT_ADM8668; + } +} + +/* + * serial core request to check that port information in serinfo are + * suitable + */ +static int adm_uart_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + if (port->type != PORT_ADM8668) + return -EINVAL; + if (port->irq != serinfo->irq) + return -EINVAL; + if (port->iotype != serinfo->io_type) + return -EINVAL; + if (port->mapbase != (unsigned long)serinfo->iomem_base) + return -EINVAL; + return 0; +} + +/* serial core callbacks */ +static struct uart_ops adm_uart_ops = { + .tx_empty = adm_uart_tx_empty, + .get_mctrl = adm_uart_get_mctrl, + .set_mctrl = adm_uart_set_mctrl, + .start_tx = adm_uart_start_tx, + .stop_tx = adm_uart_stop_tx, + .stop_rx = adm_uart_stop_rx, + .enable_ms = adm_uart_enable_ms, + .break_ctl = adm_uart_break_ctl, + .startup = adm_uart_startup, + .shutdown = adm_uart_shutdown, + .set_termios = adm_uart_set_termios, + .type = adm_uart_type, + .release_port = adm_uart_release_port, + .request_port = adm_uart_request_port, + .config_port = adm_uart_config_port, + .verify_port = adm_uart_verify_port, +}; + +#ifdef CONFIG_SERIAL_ADM8668_CONSOLE +static inline void wait_for_xmitr(struct uart_port *port) +{ + while ((adm_uart_readl(port, UART_FR_REG) & UART_TX_FIFO_FULL) != 0) + ; +} + +/* + * output given char + */ +static void adm_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + adm_uart_writel(port, ch, UART_DR_REG); +} + +/* + * console core request to output given string + */ +static void adm_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port; + unsigned long flags; + int locked; + + port = &ports[co->index]; + + local_irq_save(flags); + if (port->sysrq) { + /* adm_uart_interrupt() already took the lock */ + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&port->lock); + } else { + spin_lock(&port->lock); + locked = 1; + } + + /* call helper to deal with \r\n */ + uart_console_write(port, s, count, adm_console_putchar); + + /* and wait for char to be transmitted */ + wait_for_xmitr(port); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +/* + * console core request to setup given console, find matching uart + * port and setup it. + */ +static int adm_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= ADM8668_NR_UARTS) + return -EINVAL; + port = &ports[co->index]; + if (!port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver adm_uart_driver; + +static struct console adm8668_console = { + .name = "ttyS", + .write = adm_console_write, + .device = uart_console_device, + .setup = adm_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &adm_uart_driver, +}; + +static int __init adm8668_console_init(void) +{ + register_console(&adm8668_console); + return 0; +} + +console_initcall(adm8668_console_init); + +#define ADM8668_CONSOLE (&adm8668_console) +#else +#define ADM8668_CONSOLE NULL +#endif /* CONFIG_SERIAL_ADM8668_CONSOLE */ + +static struct uart_driver adm_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "adm8668_uart", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = 1, + .cons = ADM8668_CONSOLE, +}; + +/* + * platform driver probe/remove callback + */ +static int __devinit adm_uart_probe(struct platform_device *pdev) +{ + struct resource *res_mem, *res_irq; + struct uart_port *port; + int ret; + + if (pdev->id < 0 || pdev->id >= ADM8668_NR_UARTS) + return -EINVAL; + + if (ports[pdev->id].membase) + return -EBUSY; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) + return -ENODEV; + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_irq) + return -ENODEV; + + port = &ports[pdev->id]; + memset(port, 0, sizeof(*port)); + port->iotype = UPIO_MEM; + port->mapbase = res_mem->start; + port->irq = res_irq->start; + port->ops = &adm_uart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->dev = &pdev->dev; + port->fifosize = 8; + port->uartclk = ADM8668_UARTCLK_FREQ; + + ret = uart_add_one_port(&adm_uart_driver, port); + if (ret) { + ports[pdev->id].membase = 0; + return ret; + } + platform_set_drvdata(pdev, port); + return 0; +} + +static int __devexit adm_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + + port = platform_get_drvdata(pdev); + uart_remove_one_port(&adm_uart_driver, port); + platform_set_drvdata(pdev, NULL); + /* mark port as free */ + ports[pdev->id].membase = 0; + return 0; +} + +/* + * platform driver stuff + */ +static struct platform_driver adm_uart_platform_driver = { + .probe = adm_uart_probe, + .remove = __devexit_p(adm_uart_remove), + .driver = { + .owner = THIS_MODULE, + .name = "adm8668_uart", + }, +}; + +static int __init adm_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&adm_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&adm_uart_platform_driver); + if (ret) + uart_unregister_driver(&adm_uart_driver); + + return ret; +} + +static void __exit adm_uart_exit(void) +{ + platform_driver_unregister(&adm_uart_platform_driver); + uart_unregister_driver(&adm_uart_driver); +} + +module_init(adm_uart_init); +module_exit(adm_uart_exit); + +MODULE_AUTHOR("Scott Nicholas <neutronscott@scottn.us>"); +MODULE_DESCRIPTION("ADM8668 integrated uart driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/adm8668/files/arch/mips/adm8668/u-boot.h b/target/linux/adm8668/files/arch/mips/adm8668/u-boot.h new file mode 100644 index 000000000..d9d226889 --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/adm8668/u-boot.h @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, <wd@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#ifndef _U_BOOT_H_ +#define _U_BOOT_H_ 1 + +typedef struct bd_info { + int bi_baudrate; /* serial console baudrate */ + unsigned long bi_ip_addr; /* IP Address */ + unsigned char bi_enetaddr[6]; /* Ethernet adress */ + unsigned long bi_arch_number; /* unique id for this board */ + unsigned long bi_boot_params; /* where this board expects params */ + unsigned long bi_memstart; /* start of DRAM memory */ + unsigned long bi_memsize; /* size of DRAM memory in bytes */ + unsigned long bi_flashstart; /* start of FLASH memory */ + unsigned long bi_flashsize; /* size of FLASH memory */ + unsigned long bi_flashoffset; /* reserved area for startup monitor */ +} bd_t; + +struct global_data { + bd_t *bd; /* board data... */ + unsigned long flags; + unsigned long baudrate; + unsigned long have_console; /* serial_init() was called */ + unsigned long ram_size; /* RAM size */ + unsigned long reloc_off; /* Relocation Offset */ + unsigned long env_addr; /* Address of Environment struct */ + unsigned long env_valid; /* Checksum of Environment valid? */ + void **jt; /* jump table */ +}; + +#endif /* _U_BOOT_H_ */ diff --git a/target/linux/adm8668/files/arch/mips/include/asm/mach-adm8668/adm8668.h b/target/linux/adm8668/files/arch/mips/include/asm/mach-adm8668/adm8668.h new file mode 100644 index 000000000..2cf65d81a --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/include/asm/mach-adm8668/adm8668.h @@ -0,0 +1,139 @@ +/************************************************************************ + * + * Copyright (c) 2005 + * Infineon Technologies AG + * St. Martin Strasse 53; 81669 Muenchen; Germany + * + ************************************************************************/ + +#ifndef __ADM8668_H__ +#define __ADM8668_H__ + +#define SYS_CLOCK 175000000 + +/*======================= Physical Memory Map ============================*/ +#define ADM8668_SDRAM_BASE 0 +#define ADM8668_SMEM1_BASE 0x10000000 +#define ADM8668_MPMC_BASE 0x11000000 +#define ADM8668_USB_BASE 0x11200000 +#define ADM8668_CONFIG_BASE 0x11400000 +#define ADM8668_WAN_BASE 0x11600000 +#define ADM8668_WLAN_BASE 0x11800000 +#define ADM8668_LAN_BASE 0x11A00000 +#define ADM8668_INTC_BASE 0x1E000000 +#define ADM8668_TMR_BASE 0x1E200000 +#define ADM8668_UART0_BASE 0x1E400000 +#define ADM8668_SMEM0_BASE 0x1FC00000 +#define ADM8668_NAND_BASE 0x1FFFFF00 + +#define PCICFG_BASE 0x12200000 +#define PCIDAT_BASE 0x12400000 + +/** onboard uart **/ +#define ADM8668_UARTCLK_FREQ 62500000 +/* registers */ +#define UART_DR_REG 0x00 +#define UART_RSR_REG 0x04 +#define UART_CR_REG 0x14 +#define UART_FR_REG 0x18 +#define UART_IIR_REG 0x1C + +/* rsr reg */ +#define UART_FRAMING_ERR 0x01 +#define UART_PARITY_ERR 0x02 +#define UART_BREAK_ERR 0x04 +#define UART_OVERRUN_ERR 0x08 +#define UART_RX_STATUS_MASK 0x0F + +/* cr reg */ +#define UART_RX_INT_EN 0x10 +#define UART_TX_INT_EN 0x20 +#define UART_RX_TIMEOUT_INT_EN 0x40 + +/* fr reg */ +#define UART_RX_FIFO_EMPTY 0x10 +#define UART_TX_FIFO_FULL 0x20 + +/* iir reg */ +#define UART_RX_INT 0x02 +#define UART_TX_INT 0x04 +#define UART_RX_TIMEOUT_INT 0x08 + +/* interrupt controller */ +#define IRQ_STATUS_REG 0x00 /* Read */ +#define IRQ_ENABLE_REG 0x08 /* Read/Write */ +#define IRQ_DISABLE_REG 0x0C /* Write */ + +/* interrupt levels */ +#define INT_LVL_SWI 1 +#define INT_LVL_COMMS_RX 2 +#define INT_LVL_COMMS_TX 3 +#define INT_LVL_TIMERINT0 4 +#define INT_LVL_TIMERINT1 5 +#define INT_LVL_UART0 6 +#define INT_LVL_LAN 7 +#define INT_LVL_WAN 8 +#define INT_LVL_WLAN 9 +#define INT_LVL_GPIO 10 +#define INT_LVL_IDE 11 +#define INT_LVL_PCI2 12 +#define INT_LVL_PCI1 13 +#define INT_LVL_PCI0 14 +#define INT_LVL_USB 15 +#define INT_LVL_MAX INT_LVL_USB + +/* register access macros */ +#define ADM8668_INTC_REG(_reg) \ + (*((volatile unsigned long *)(KSEG1ADDR(ADM8668_INTC_BASE + (_reg))))) +#define ADM8668_LAN_REG(_reg) \ + (*((volatile unsigned int *)(KSEG1ADDR(ADM8668_LAN_BASE + (_reg))))) +#define ADM8668_WAN_REG(_reg) \ + (*((volatile unsigned int *)(KSEG1ADDR(ADM8668_WAN_BASE + (_reg))))) +#define ADM8668_WLAN_REG(_reg) \ + (*((volatile unsigned int *)(KSEG1ADDR(ADM8668_WLAN_BASE + (_reg))))) +#define ADM8668_CONFIG_REG(_reg) \ + (*((volatile unsigned int *)(KSEG1ADDR(ADM8668_CONFIG_BASE + (_reg))))) + +/* lan registers */ +#define NETCSR6 0x30 +#define NETCSR7 0x38 +#define NETCSR37 0xF8 + +/* known/used CPU configuration registers */ +#define ADM8668_CR0 0x00 +#define ADM8668_CR1 0x04 +#define ADM8668_CR3 0x0C + +/** For GPIO control **/ +#define GPIO_REG 0x5C /* on WLAN */ +#define CRGPIO_REG 0x20 /* on CPU */ +#define GPIO0_OUTPUT_ENABLE 0x1000 +#define GPIO1_OUTPUT_ENABLE 0x2000 +#define GPIO2_OUTPUT_ENABLE 0x4000 +#define GPIO_OUTPUT_ENABLE_ALL 0x7000 +#define GPIO0_OUTPUT_1 0x40 +#define GPIO1_OUTPUT_1 0x80 +#define GPIO2_OUTPUT_1 0x100 +#define GPIO0_INPUT_1 0x1 +#define GPIO1_INPUT_1 0x2 +#define GPIO2_INPUT_1 0x4 + +#define GPIO_SET_HI(num) \ + ADM8668_WLAN_REG(GPIO_REG) |= (1 << (6 + num)) + +#define GPIO_SET_LOW(num) \ + ADM8668_WLAN_REG(GPIO_REG) &= ~(1 << (6 + num)) + +#define GPIO_TOGGLE(num) \ + ADM8668_WLAN_REG(GPIO_REG) ^= (1 << (6 + num)) + +#define CRGPIO_SET_HI(num) \ + ADM8668_CONFIG_REG(CRGPIO_REG) |= (1 << (6 + num)) + +#define CRGPIO_SET_LOW(num) \ + ADM8668_CONFIG_REG(CRGPIO_REG) &= ~(1 << (6 + num)) + +#define CRGPIO_TOGGLE(num) \ + ADM8668_CONFIG_REG(CRGPIO_REG) ^= (1 << (6 + num)) + +#endif /* __ADM8668_H__ */ diff --git a/target/linux/adm8668/files/arch/mips/include/asm/mach-adm8668/irq.h b/target/linux/adm8668/files/arch/mips/include/asm/mach-adm8668/irq.h new file mode 100644 index 000000000..1341309f2 --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/include/asm/mach-adm8668/irq.h @@ -0,0 +1,16 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2003 by Ralf Baechle + */ +#ifndef __ASM_MACH_ADM8668_IRQ_H +#define __ASM_MACH_ADM8668_IRQ_H + +#define NR_IRQS 32 +#define MIPS_CPU_IRQ_BASE 16 + +#define IRQ_MASK 0xffff + +#endif /* __ASM_MACH_ADM8668_IRQ_H */ diff --git a/target/linux/adm8668/files/arch/mips/include/asm/mach-adm8668/war.h b/target/linux/adm8668/files/arch/mips/include/asm/mach-adm8668/war.h new file mode 100644 index 000000000..7180043d3 --- /dev/null +++ b/target/linux/adm8668/files/arch/mips/include/asm/mach-adm8668/war.h @@ -0,0 +1,25 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org> + */ +#ifndef __ASM_MIPS_MACH_ADM8668_WAR_H +#define __ASM_MIPS_MACH_ADM8668_WAR_H + +#define R4600_V1_INDEX_ICACHEOP_WAR 0 +#define R4600_V1_HIT_CACHEOP_WAR 0 +#define R4600_V2_HIT_CACHEOP_WAR 0 +#define R5432_CP0_INTERRUPT_WAR 0 +#define BCM1250_M3_WAR 0 +#define SIBYTE_1956_WAR 0 +#define MIPS4K_ICACHE_REFILL_WAR 0 +#define MIPS_CACHE_SYNC_WAR 0 +#define TX49XX_ICACHE_INDEX_INV_WAR 0 +#define RM9000_CDEX_SMP_WAR 0 +#define ICACHE_REFILLS_WORKAROUND_WAR 0 +#define R10000_LLSC_WAR 0 +#define MIPS34K_MISSED_ITLB_WAR 0 + +#endif /* __ASM_MIPS_MACH_ADM8668_WAR_H */ diff --git a/target/linux/adm8668/files/drivers/mtd/maps/adm8668.c b/target/linux/adm8668/files/drivers/mtd/maps/adm8668.c new file mode 100644 index 000000000..e360a0d78 --- /dev/null +++ b/target/linux/adm8668/files/drivers/mtd/maps/adm8668.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org> + * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org) + * + * original functions for finding root filesystem from Mike Baker + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * Flash mapping for adm8668 boards + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/slab.h> +#include <linux/mtd/partitions.h> +#include <linux/crc32.h> +#include <linux/magic.h> +#include <asm/io.h> + +#define WINDOW_ADDR 0x10000000 +#define WINDOW_SIZE 0x800000 +#define BANKWIDTH 2 + +/* first a little bit about the headers i need.. */ + +/* just interested in part of the full struct */ +struct squashfs_super_block { + __le32 s_magic; + __le32 pad0[9]; /* it's not really padding */ + __le64 bytes_used; +}; + +#define IH_MAGIC 0x56190527 /* Image Magic Number */ +struct uboot_header { + uint32_t ih_magic; /* Image Header Magic Number */ + uint32_t ih_hcrc; /* Image Header CRC Checksum */ + uint32_t ih_time; /* Image Creation Timestamp */ + uint32_t ih_size; /* Image Data Size */ + uint32_t ih_load; /* Data Load Address */ + uint32_t ih_ep; /* Entry Point Address */ + uint32_t ih_dcrc; /* Image Data CRC Checksum */ + uint8_t ih_os; /* Operating System */ + uint8_t ih_arch; /* CPU architecture */ + uint8_t ih_type; /* Image Type */ + uint8_t ih_comp; /* Compression Type */ + char ih_name[32]; /* image name */ +}; + +/************************************************/ + +static struct mtd_info *adm8668_mtd; + +struct map_info adm8668_map = { + name: "adm8668-nor", + size: WINDOW_SIZE, + phys: WINDOW_ADDR, + bankwidth: BANKWIDTH, +}; + +/* + * Copied from mtdblock.c + * + * Cache stuff... + * + * Since typical flash erasable sectors are much larger than what Linux's + * buffer cache can handle, we must implement read-modify-write on flash + * sectors for each block write requests. To avoid over-erasing flash sectors + * and to speed things up, we locally cache a whole flash sector while it is + * being written to until a different sector is required. + */ + +static void erase_callback(struct erase_info *done) +{ + wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; + wake_up(wait_q); +} + +static int erase_write (struct mtd_info *mtd, unsigned long pos, + int len, const char *buf) +{ + struct erase_info erase; + DECLARE_WAITQUEUE(wait, current); + wait_queue_head_t wait_q; + size_t retlen; + int ret; + + /* + * First, let's erase the flash block. + */ + + init_waitqueue_head(&wait_q); + erase.mtd = mtd; + erase.callback = erase_callback; + erase.addr = pos; + erase.len = len; + erase.priv = (u_long)&wait_q; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&wait_q, &wait); + + ret = mtd->erase(mtd, &erase); + if (ret) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + printk (KERN_WARNING "erase of region [0x%lx, 0x%x] " + "on \"%s\" failed\n", + pos, len, mtd->name); + return ret; + } + + schedule(); /* Wait for erase to finish. */ + remove_wait_queue(&wait_q, &wait); + + /* + * Next, write data to flash. + */ + + ret = mtd->write (mtd, pos, len, &retlen, buf); + if (ret) + return ret; + if (retlen != len) + return -EIO; + return 0; +} + +/* decent defaults in case... shrug */ +static struct mtd_partition adm8668_parts[] = { + { name: "linux", offset: 0x40000, size: WINDOW_SIZE-0x40000, }, + { name: "rootfs", offset: 0xe0000, size: 0x140000, }, + { name: "uboot_env", offset: 0x20000, size: 0x20000, }, + { name: NULL, }, +}; + +/* in case i wanna change stuff later, and to clarify the math section... */ +#define PART_LINUX 0 +#define PART_ROOTFS 1 +#define NR_PARTS 3 + +static int __init +init_mtd_partitions(struct mtd_info *mtd, size_t size) +{ + struct uboot_header uhdr; + int off, blocksize; + size_t len, linux_len; + struct squashfs_super_block shdr; + + blocksize = mtd->erasesize; + if (blocksize < 0x10000) + blocksize = 0x10000; + + /* now find squashfs */ + memset(&shdr, 0xe5, sizeof(shdr)); + for (off = adm8668_parts[PART_LINUX].offset; off < size; off += blocksize) { + /* + * Read into buffer + */ + if (mtd->read(mtd, off, sizeof(shdr), &len, (char *)&shdr) || + len != sizeof(shdr)) + continue; + + if (shdr.s_magic == SQUASHFS_MAGIC) { + uint32_t fs_size = (uint32_t)shdr.bytes_used; + + printk(KERN_INFO "%s: Filesystem type: squashfs, size=%dkB\n", + mtd->name, fs_size>>10); + + /* Update rootfs based on the superblock info, and + * stretch to end of MTD. rootfs_split will split it */ + adm8668_parts[PART_ROOTFS].offset = off; + adm8668_parts[PART_ROOTFS].size = mtd->size - + adm8668_parts[PART_ROOTFS].offset; + + /* kernel ends where rootfs starts + * but we'll keep it full-length for upgrades */ + linux_len = adm8668_parts[PART_LINUX+1].offset - + adm8668_parts[PART_LINUX].offset; +#if 1 + adm8668_parts[PART_LINUX].size = mtd->size - + adm8668_parts[PART_LINUX].offset; +#else + adm8668_parts[PART_LINUX].size = linux_len; +#endif + goto found; + } + } + + printk(KERN_NOTICE + "%s: Couldn't find root filesystem\n", + mtd->name); + return NR_PARTS; + + found: + if (mtd->read(mtd, adm8668_parts[PART_LINUX].offset, sizeof(uhdr), &len, (char *)&uhdr) || + len != sizeof(uhdr)) + return NR_PARTS; + + /* that's odd. how'd ya boot it then */ + if (uhdr.ih_magic != IH_MAGIC) + return NR_PARTS; + + if (be32_to_cpu(uhdr.ih_size) != (linux_len - sizeof(uhdr))) { + unsigned char *block, *data; + unsigned int offset; + + offset = adm8668_parts[PART_LINUX].offset + + sizeof(struct uboot_header); + data = (unsigned char *)(WINDOW_ADDR | 0xA0000000 | offset); + + printk(KERN_NOTICE "Updating U-boot image:\n"); + printk(KERN_NOTICE " old: [size: %8d crc32: 0x%08x]\n", + be32_to_cpu(uhdr.ih_size), be32_to_cpu(uhdr.ih_dcrc)); + + /* Update the data length & crc32 */ + uhdr.ih_size = cpu_to_be32(linux_len - sizeof(uhdr)); + uhdr.ih_dcrc = crc32_le(~0, data, linux_len - sizeof(uhdr)) ^ (~0); + uhdr.ih_dcrc = cpu_to_be32(uhdr.ih_dcrc); + + printk(KERN_NOTICE " new: [size: %8d crc32: 0x%08x]\n", + be32_to_cpu(uhdr.ih_size), be32_to_cpu(uhdr.ih_dcrc)); + + /* update header's crc... */ + uhdr.ih_hcrc = 0; + uhdr.ih_hcrc = crc32_le(~0, (unsigned char *)&uhdr, + sizeof(uhdr)) ^ (~0); + uhdr.ih_hcrc = cpu_to_be32(uhdr.ih_hcrc); + + /* read first eraseblock from the image */ + block = kmalloc(mtd->erasesize, GFP_KERNEL); + if (mtd->read(mtd, adm8668_parts[PART_LINUX].offset, mtd->erasesize, &len, block) || len != mtd->erasesize) { + printk("Error copying first eraseblock\n"); + return 0; + } + + /* Write updated header to the flash */ + memcpy(block, &uhdr, sizeof(uhdr)); + if (mtd->unlock) + mtd->unlock(mtd, off, mtd->erasesize); + erase_write(mtd, adm8668_parts[PART_LINUX].offset, mtd->erasesize, block); + if (mtd->sync) + mtd->sync(mtd); + kfree(block); + printk(KERN_NOTICE "Done\n"); + } + + return NR_PARTS; +} + +int __init init_adm8668_map(void) +{ + int nr_parts, ret; + + adm8668_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); + + if (!adm8668_map.virt) { + printk(KERN_ERR "Failed to ioremap\n"); + return -EIO; + } + + simple_map_init(&adm8668_map); + if (!(adm8668_mtd = do_map_probe("cfi_probe", &adm8668_map))) { + printk(KERN_ERR "cfi_probe failed\n"); + iounmap((void *)adm8668_map.virt); + return -ENXIO; + } + + adm8668_mtd->owner = THIS_MODULE; + + nr_parts = init_mtd_partitions(adm8668_mtd, adm8668_mtd->size); + ret = mtd_device_register(adm8668_mtd, adm8668_parts, nr_parts); + if (ret) { + printk(KERN_ERR "Flash: mtd_device_register failed\n"); + goto fail; + } + + return 0; + + fail: + if (adm8668_mtd) + map_destroy(adm8668_mtd); + if (adm8668_map.virt) + iounmap((void *) adm8668_map.virt); + adm8668_map.virt = 0; + return ret; +} + +void __exit cleanup_adm8668_map(void) +{ + mtd_device_unregister(adm8668_mtd); + map_destroy(adm8668_mtd); + iounmap((void *) adm8668_map.virt); + adm8668_map.virt = 0; +} + +module_init(init_adm8668_map); +module_exit(cleanup_adm8668_map); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Scott Nicholas <neutronscott@scottn.us>"); +MODULE_DESCRIPTION("MTD map driver for ADM8668 NOR Flash"); |