surprise :p

SVN-Revision: 11894
This commit is contained in:
Gabor Juhos 2008-07-21 17:08:14 +00:00
parent 3d9c4c9073
commit f529a37420
54 changed files with 6655 additions and 18 deletions

View file

@ -0,0 +1,24 @@
#
# Copyright (C) 2008 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
ARCH:=mips
BOARD:=ar71xx
BOARDNAME:=Atheros AR71xx
FEATURES:=squashfs tgz broken
LINUX_VERSION:=2.6.25.10
include $(INCLUDE_DIR)/target.mk
#DEFAULT_PACKAGES += kmod-madwifi
define Target/Description
Build firmware images for Atheros AR71xx based boards
endef
$(eval $(call BuildTarget))

View file

@ -0,0 +1,244 @@
CONFIG_32BIT=y
# CONFIG_64BIT is not set
# CONFIG_8139TOO is not set
CONFIG_ADM6996_PHY=y
CONFIG_AG71XX=y
# CONFIG_AR71XX_EARLY_SERIAL is not set
CONFIG_AR71XX_MACH_GENERIC=y
CONFIG_AR71XX_MACH_RB_4XX=y
CONFIG_AR71XX_MACH_WP543=y
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_ARCH_POPULATES_NODE_MAP=y
# CONFIG_ARCH_SUPPORTS_MSI is not set
CONFIG_ARCH_SUPPORTS_OPROFILE=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_ATHEROS_AR71XX=y
CONFIG_BASE_SMALL=0
# CONFIG_BCM47XX is not set
CONFIG_BITREVERSE=y
# CONFIG_BROADCOM_PHY is not set
CONFIG_CEVT_R4K=y
CONFIG_CLASSIC_RCU=y
CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=squashfs,yaffs,jffs2 noinitrd console=ttyS0,115200 init=/etc/preinit"
CONFIG_CPU_BIG_ENDIAN=y
CONFIG_CPU_HAS_LLSC=y
CONFIG_CPU_HAS_PREFETCH=y
CONFIG_CPU_HAS_SYNC=y
# CONFIG_CPU_LITTLE_ENDIAN is not set
# CONFIG_CPU_LOONGSON2 is not set
CONFIG_CPU_MIPS32=y
# CONFIG_CPU_MIPS32_R1 is not set
CONFIG_CPU_MIPS32_R2=y
# CONFIG_CPU_MIPS64_R1 is not set
# CONFIG_CPU_MIPS64_R2 is not set
CONFIG_CPU_MIPSR2=y
# CONFIG_CPU_NEVADA is not set
# CONFIG_CPU_R10000 is not set
# CONFIG_CPU_R3000 is not set
# CONFIG_CPU_R4300 is not set
# CONFIG_CPU_R4X00 is not set
# CONFIG_CPU_R5000 is not set
# CONFIG_CPU_R5432 is not set
# CONFIG_CPU_R6000 is not set
# CONFIG_CPU_R8000 is not set
# CONFIG_CPU_RM7000 is not set
# CONFIG_CPU_RM9000 is not set
# CONFIG_CPU_SB1 is not set
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
CONFIG_CPU_SUPPORTS_HIGHMEM=y
# CONFIG_CPU_TX39XX is not set
# CONFIG_CPU_TX49XX is not set
# CONFIG_CPU_VR41XX is not set
CONFIG_CRYPTO_AEAD=m
CONFIG_CRYPTO_AUTHENC=m
CONFIG_CRYPTO_GF128MUL=m
CONFIG_CSRC_R4K=y
CONFIG_DEVPORT=y
# CONFIG_DM9000 is not set
CONFIG_DMA_NEED_PCI_MAP_STATE=y
CONFIG_DMA_NONCOHERENT=y
# CONFIG_E1000E_ENABLED is not set
CONFIG_EARLY_PRINTK=y
# CONFIG_FIXED_PHY is not set
CONFIG_FS_POSIX_ACL=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_FIND_NEXT_BIT=y
CONFIG_GENERIC_GPIO=y
# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set
# CONFIG_GPIO_MCP23S08 is not set
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAVE_GPIO_LIB=y
CONFIG_HAVE_IDE=y
# CONFIG_HAVE_KPROBES is not set
# CONFIG_HAVE_KRETPROBES is not set
CONFIG_HAVE_OPROFILE=y
CONFIG_HW_HAS_PCI=y
CONFIG_HW_RANDOM=m
# CONFIG_I2C is not set
# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
# CONFIG_IBM_NEW_EMAC_RGMII is not set
# CONFIG_IBM_NEW_EMAC_TAH is not set
# CONFIG_IBM_NEW_EMAC_ZMII is not set
CONFIG_ICPLUS_PHY=y
# CONFIG_IDE is not set
CONFIG_INITRAMFS_ROOT_GID=0
CONFIG_INITRAMFS_ROOT_UID=0
CONFIG_INITRAMFS_SOURCE="../../root"
CONFIG_IRQ_CPU=y
# CONFIG_LEDS_ALIX is not set
CONFIG_LEDS_GPIO=y
# CONFIG_LEMOTE_FULONG is not set
CONFIG_LZO_COMPRESS=m
CONFIG_LZO_DECOMPRESS=m
# CONFIG_MACH_ALCHEMY is not set
# CONFIG_MACH_DECSTATION is not set
# CONFIG_MACH_JAZZ is not set
# CONFIG_MACH_VR41XX is not set
# CONFIG_MDIO_BITBANG is not set
# CONFIG_MEMSTICK is not set
CONFIG_MICREL_PHY=y
CONFIG_MIPS=y
# CONFIG_MIPS_ATLAS is not set
# CONFIG_MIPS_COBALT is not set
CONFIG_MIPS_L1_CACHE_SHIFT=5
CONFIG_MIPS_MACHINE=y
# CONFIG_MIPS_MALTA is not set
CONFIG_MIPS_MT_DISABLED=y
# CONFIG_MIPS_MT_SMP is not set
# CONFIG_MIPS_MT_SMTC is not set
# CONFIG_MIPS_SEAD is not set
# CONFIG_MIPS_SIM is not set
CONFIG_MTD=y
# CONFIG_MTD_ABSENT is not set
CONFIG_MTD_BLKDEVS=y
CONFIG_MTD_BLOCK=y
# CONFIG_MTD_BLOCK2MTD is not set
# CONFIG_MTD_CFI is not set
CONFIG_MTD_CFI_I1=y
CONFIG_MTD_CFI_I2=y
# CONFIG_MTD_CFI_I4 is not set
# CONFIG_MTD_CFI_I8 is not set
CONFIG_MTD_CHAR=y
# CONFIG_MTD_CMDLINE_PARTS is not set
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
CONFIG_MTD_CONCAT=y
# CONFIG_MTD_DEBUG is not set
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
# CONFIG_MTD_JEDECPROBE is not set
CONFIG_MTD_M25P80=y
CONFIG_MTD_MAP_BANK_WIDTH_1=y
# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
CONFIG_MTD_MAP_BANK_WIDTH_2=y
# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
CONFIG_MTD_MAP_BANK_WIDTH_4=y
# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
# CONFIG_MTD_MTDRAM is not set
CONFIG_MTD_NAND=y
# CONFIG_MTD_NAND_CAFE is not set
# CONFIG_MTD_NAND_DISKONCHIP is not set
# CONFIG_MTD_NAND_ECC_SMC is not set
CONFIG_MTD_NAND_IDS=y
# CONFIG_MTD_NAND_MUSEUM_IDS is not set
# CONFIG_MTD_NAND_NANDSIM is not set
# CONFIG_MTD_NAND_PLATFORM is not set
CONFIG_MTD_NAND_RB4XX=y
# CONFIG_MTD_NAND_VERIFY_WRITE is not set
# CONFIG_MTD_ONENAND is not set
CONFIG_MTD_PARTITIONS=y
# CONFIG_MTD_PHRAM is not set
# CONFIG_MTD_PLATRAM is not set
# CONFIG_MTD_PMC551 is not set
# CONFIG_MTD_RAM is not set
CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-2
CONFIG_MTD_REDBOOT_PARTS=y
CONFIG_MTD_REDBOOT_PARTS_READONLY=y
CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y
# CONFIG_MTD_ROM is not set
# CONFIG_MTD_SLRAM is not set
# CONFIG_NATSEMI is not set
# CONFIG_NE2K_PCI is not set
# CONFIG_NET_VENDOR_3COM is not set
# CONFIG_NO_IOPORT is not set
# CONFIG_OCF_OCF is not set
# CONFIG_PAGE_SIZE_16KB is not set
CONFIG_PAGE_SIZE_4KB=y
# CONFIG_PAGE_SIZE_64KB is not set
# CONFIG_PAGE_SIZE_8KB is not set
CONFIG_PCI=y
# CONFIG_PCIPCWATCHDOG is not set
CONFIG_PCI_DOMAINS=y
CONFIG_PHYLIB=y
# CONFIG_PMC_MSP is not set
# CONFIG_PMC_YOSEMITE is not set
# CONFIG_PNX8550_JBS is not set
# CONFIG_PNX8550_STB810 is not set
# CONFIG_QSEMI_PHY is not set
# CONFIG_R6040 is not set
# CONFIG_REALTEK_PHY is not set
# CONFIG_RTC is not set
CONFIG_RTC_LIB=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
CONFIG_SCSI_WAIT_SCAN=m
# CONFIG_SERIAL_8250_EXTENDED is not set
CONFIG_SERIAL_8250_NR_UARTS=1
CONFIG_SERIAL_8250_RUNTIME_UARTS=1
# CONFIG_SGI_IP22 is not set
# CONFIG_SGI_IP27 is not set
# CONFIG_SGI_IP28 is not set
# CONFIG_SGI_IP32 is not set
# CONFIG_SIBYTE_BIGSUR is not set
# CONFIG_SIBYTE_CARMEL is not set
# CONFIG_SIBYTE_CRHINE is not set
# CONFIG_SIBYTE_CRHONE is not set
# CONFIG_SIBYTE_LITTLESUR is not set
# CONFIG_SIBYTE_RHONE is not set
# CONFIG_SIBYTE_SENTOSA is not set
# CONFIG_SIBYTE_SWARM is not set
CONFIG_SLABINFO=y
# CONFIG_SMSC_PHY is not set
# CONFIG_SOFT_WATCHDOG is not set
# CONFIG_SPARSEMEM_STATIC is not set
# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
CONFIG_SPI=y
CONFIG_SPI_AR71XX=y
CONFIG_SPI_BITBANG=y
# CONFIG_SPI_GPIO is not set
CONFIG_SPI_MASTER=y
# CONFIG_SPI_SPIDEV is not set
CONFIG_SSB_POSSIBLE=y
CONFIG_SYSVIPC_SYSCTL=y
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
CONFIG_SYS_HAS_CPU_MIPS32_R2=y
CONFIG_SYS_HAS_EARLY_PRINTK=y
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y
# CONFIG_TC35815 is not set
# CONFIG_THERMAL is not set
CONFIG_TICK_ONESHOT=y
# CONFIG_TOSHIBA_JMR3927 is not set
# CONFIG_TOSHIBA_RBTX4927 is not set
# CONFIG_TOSHIBA_RBTX4938 is not set
CONFIG_TRAD_SIGNALS=y
# CONFIG_VGASTATE is not set
# CONFIG_VIA_RHINE is not set
CONFIG_VIDEO_V4L2_COMMON=m
CONFIG_YAFFS_9BYTE_TAGS=y
CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED=y
CONFIG_YAFFS_AUTO_YAFFS2=y
CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=0
# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
CONFIG_YAFFS_FS=y
CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
CONFIG_YAFFS_YAFFS1=y
CONFIG_YAFFS_YAFFS2=y
CONFIG_ZONE_DMA_FLAG=0

View file

@ -0,0 +1,23 @@
if ATHEROS_AR71XX
config AR71XX_EARLY_SERIAL
bool "Use early serial console"
default n
menu "Atheros AR71xx machine selection"
config AR71XX_MACH_GENERIC
bool "Generic AR71xx based machine support"
default y
config AR71XX_MACH_WP543
bool "Compex WP543 board support"
default y
config AR71XX_MACH_RB_4XX
bool "MikroTik RouterBOARD 4xx series support"
default y
endmenu
endif

View file

@ -0,0 +1,15 @@
#
# Makefile for the Atheros AR71xx SoC specific parts of the kernel
#
# Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
# Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
obj-y := prom.o irq.o setup.o platform.o gpio.o ar71xx.o
obj-$(CONFIG_AR71XX_MACH_GENERIC) += mach-generic.o
obj-$(CONFIG_AR71XX_MACH_RB_4XX) += mach-rb-4xx.o
obj-$(CONFIG_AR71XX_MACH_WP543) += mach-wp543.o

View file

@ -0,0 +1,55 @@
/*
* AR71xx SoC routines
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <asm/mach-ar71xx/ar71xx.h>
void __iomem *ar71xx_ddr_base;
void __iomem *ar71xx_pll_base;
void __iomem *ar71xx_reset_base;
void __iomem *ar71xx_gpio_base;
void __iomem *ar71xx_usb_ctrl_base;
void ar71xx_device_stop(u32 mask)
{
unsigned long flags;
local_irq_save(flags);
ar71xx_reset_wr(RESET_REG_RESET_MODULE,
ar71xx_reset_rr(RESET_REG_RESET_MODULE) | mask);
local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(ar71xx_device_stop);
void ar71xx_device_start(u32 mask)
{
unsigned long flags;
local_irq_save(flags);
ar71xx_reset_wr(RESET_REG_RESET_MODULE,
ar71xx_reset_rr(RESET_REG_RESET_MODULE) & ~mask);
local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(ar71xx_device_start);
void ar71xx_ddr_flush(u32 reg)
{
ar71xx_ddr_wr(reg, 1);
while ((ar71xx_ddr_rr(reg) & 0x1));
ar71xx_ddr_wr(reg, 1);
while ((ar71xx_ddr_rr(reg) & 0x1));
}
EXPORT_SYMBOL_GPL(ar71xx_ddr_flush);

View file

@ -0,0 +1,135 @@
/*
* Atheros AR71xx SoC GPIO API support
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/gpio.h>
#include <asm/mach-ar71xx/ar71xx.h>
static DEFINE_SPINLOCK(ar71xx_gpio_lock);
void __ar71xx_gpio_set_value(unsigned gpio, int value)
{
unsigned long flags;
spin_lock_irqsave(&ar71xx_gpio_lock, flags);
if (value)
ar71xx_gpio_wr(GPIO_REG_SET, (1 << gpio));
else
ar71xx_gpio_wr(GPIO_REG_CLEAR, (1 << gpio));
spin_unlock_irqrestore(&ar71xx_gpio_lock, flags);
}
EXPORT_SYMBOL(__ar71xx_gpio_set_value);
int __ar71xx_gpio_get_value(unsigned gpio)
{
return (ar71xx_gpio_rr(GPIO_REG_IN) & (1 << gpio)) ? 1 : 0;
}
EXPORT_SYMBOL(__ar71xx_gpio_get_value);
static int ar71xx_gpio_get_value(struct gpio_chip *chip, unsigned offset)
{
return __ar71xx_gpio_get_value(offset);
}
static void ar71xx_gpio_set_value(struct gpio_chip *chip,
unsigned offset, int value)
{
__ar71xx_gpio_set_value(offset, value);
}
static int ar71xx_gpio_direction_input(struct gpio_chip *chip,
unsigned offset)
{
unsigned long flags;
spin_lock_irqsave(&ar71xx_gpio_lock, flags);
ar71xx_gpio_wr(GPIO_REG_OE,
ar71xx_gpio_rr(GPIO_REG_OE) & ~(1 << offset));
spin_unlock_irqrestore(&ar71xx_gpio_lock, flags);
return 0;
}
static int ar71xx_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
unsigned long flags;
spin_lock_irqsave(&ar71xx_gpio_lock, flags);
if (value)
ar71xx_gpio_wr(GPIO_REG_SET, (1 << offset));
else
ar71xx_gpio_wr(GPIO_REG_CLEAR, (1 << offset));
ar71xx_gpio_wr(GPIO_REG_OE,
ar71xx_gpio_rr(GPIO_REG_OE) | (1 << offset));
spin_unlock_irqrestore(&ar71xx_gpio_lock, flags);
return 0;
}
static struct gpio_chip ar71xx_gpio_chip = {
.label = "ar71xx",
.get = ar71xx_gpio_get_value,
.set = ar71xx_gpio_set_value,
.direction_input = ar71xx_gpio_direction_input,
.direction_output = ar71xx_gpio_direction_output,
.base = 0,
.ngpio = AR71XX_GPIO_COUNT,
};
void ar71xx_gpio_function_enable(u32 mask)
{
unsigned long flags;
spin_lock_irqsave(&ar71xx_gpio_lock, flags);
ar71xx_gpio_wr(GPIO_REG_FUNC, ar71xx_gpio_rr(GPIO_REG_FUNC) | mask);
spin_unlock_irqrestore(&ar71xx_gpio_lock, flags);
}
void ar71xx_gpio_function_disable(u32 mask)
{
unsigned long flags;
spin_lock_irqsave(&ar71xx_gpio_lock, flags);
ar71xx_gpio_wr(GPIO_REG_FUNC, ar71xx_gpio_rr(GPIO_REG_FUNC) & ~mask);
spin_unlock_irqrestore(&ar71xx_gpio_lock, flags);
}
void __init ar71xx_gpio_init(void)
{
int err;
if (!request_mem_region(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE,
"AR71xx GPIO controller"))
panic("cannot allocate AR71xx GPIO registers page");
err = gpiochip_add(&ar71xx_gpio_chip);
if (err)
panic("cannot add AR71xx GPIO chip, error=%d", err);
}

View file

@ -0,0 +1,285 @@
/*
* Atheros AR71xx SoC specific interrupt handling
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <asm/irq_cpu.h>
#include <asm/mipsregs.h>
#include <asm/mach-ar71xx/ar71xx.h>
#ifdef CONFIG_PCI
static void ar71xx_pci_irq_dispatch(void)
{
u32 pending;
pending = ar71xx_reset_rr(RESET_REG_PCI_INT_STATUS) &
ar71xx_reset_rr(RESET_REG_PCI_INT_ENABLE);
if (pending & PCI_INT_DEV0)
do_IRQ(AR71XX_PCI_IRQ_DEV0);
else if (pending & PCI_INT_DEV1)
do_IRQ(AR71XX_PCI_IRQ_DEV1);
else if (pending & PCI_INT_DEV2)
do_IRQ(AR71XX_PCI_IRQ_DEV2);
else
spurious_interrupt();
}
static void ar71xx_pci_irq_unmask(unsigned int irq)
{
irq -= AR71XX_PCI_IRQ_BASE;
ar71xx_reset_wr(RESET_REG_PCI_INT_ENABLE,
ar71xx_reset_rr(RESET_REG_PCI_INT_ENABLE) | (1 << irq));
}
static void ar71xx_pci_irq_mask(unsigned int irq)
{
irq -= AR71XX_PCI_IRQ_BASE;
ar71xx_reset_wr(RESET_REG_PCI_INT_ENABLE,
ar71xx_reset_rr(RESET_REG_PCI_INT_ENABLE) & ~(1 << irq));
}
static struct irq_chip ar71xx_pci_irq_chip = {
.name = "AR71XX PCI ",
.mask = ar71xx_pci_irq_mask,
.unmask = ar71xx_pci_irq_unmask,
.mask_ack = ar71xx_pci_irq_mask,
};
static struct irqaction ar71xx_pci_irqaction = {
.handler = no_action,
.name = "cascade [AR71XX PCI]",
};
static void __init ar71xx_pci_irq_init(void)
{
int i;
ar71xx_reset_wr(RESET_REG_PCI_INT_ENABLE, 0);
ar71xx_reset_wr(RESET_REG_PCI_INT_STATUS, 0);
for (i = AR71XX_PCI_IRQ_BASE;
i < AR71XX_PCI_IRQ_BASE + AR71XX_PCI_IRQ_COUNT; i++) {
irq_desc[i].status = IRQ_DISABLED;
set_irq_chip_and_handler(i, &ar71xx_pci_irq_chip,
handle_level_irq);
}
setup_irq(AR71XX_CPU_IRQ_PCI, &ar71xx_pci_irqaction);
}
#endif /* CONFIG_PCI */
static void ar71xx_gpio_irq_dispatch(void)
{
u32 pending;
pending = ar71xx_gpio_rr(GPIO_REG_INT_PENDING)
& ar71xx_gpio_rr(GPIO_REG_INT_ENABLE);
if (pending)
do_IRQ(AR71XX_GPIO_IRQ_BASE + fls(pending) - 1);
else
spurious_interrupt();
}
static void ar71xx_gpio_irq_unmask(unsigned int irq)
{
irq -= AR71XX_GPIO_IRQ_BASE;
ar71xx_gpio_wr(GPIO_REG_INT_ENABLE,
ar71xx_gpio_rr(GPIO_REG_INT_ENABLE) | (1 << irq));
}
static void ar71xx_gpio_irq_mask(unsigned int irq)
{
irq -= AR71XX_GPIO_IRQ_BASE;
ar71xx_gpio_wr(GPIO_REG_INT_ENABLE,
ar71xx_gpio_rr(GPIO_REG_INT_ENABLE) & ~(1 << irq));
}
#if 0
static int ar71xx_gpio_irq_set_type(unsigned int irq, unsigned int flow_type)
{
/* TODO: implement */
return 0;
}
#else
#define ar71xx_gpio_irq_set_type NULL
#endif
struct irq_chip ar71xx_gpio_irq_chip = {
.name = "AR71XX GPIO",
.unmask = ar71xx_gpio_irq_unmask,
.mask = ar71xx_gpio_irq_mask,
.mask_ack = ar71xx_gpio_irq_mask,
.set_type = ar71xx_gpio_irq_set_type,
};
static struct irqaction ar71xx_gpio_irqaction = {
.handler = no_action,
.name = "cascade [AR71XX GPIO]",
};
#define GPIO_IRQ_INIT_STATUS (IRQ_LEVEL | IRQ_TYPE_LEVEL_HIGH | IRQ_DISABLED)
#define GPIO_INT_ALL 0xffff
static void __init ar71xx_gpio_irq_init(void)
{
int i;
ar71xx_gpio_wr(GPIO_REG_INT_ENABLE, 0);
ar71xx_gpio_wr(GPIO_REG_INT_PENDING, 0);
/* setup type of all GPIO interrupts to level sensitive */
ar71xx_gpio_wr(GPIO_REG_INT_TYPE, GPIO_INT_ALL);
/* setup polarity of all GPIO interrupts to active high */
ar71xx_gpio_wr(GPIO_REG_INT_POLARITY, GPIO_INT_ALL);
for (i = AR71XX_GPIO_IRQ_BASE;
i < AR71XX_GPIO_IRQ_BASE + AR71XX_GPIO_IRQ_COUNT; i++) {
irq_desc[i].status = GPIO_IRQ_INIT_STATUS;
set_irq_chip_and_handler(i, &ar71xx_gpio_irq_chip,
handle_level_irq);
}
setup_irq(AR71XX_MISC_IRQ_GPIO, &ar71xx_gpio_irqaction);
}
static void ar71xx_misc_irq_dispatch(void)
{
u32 pending;
pending = ar71xx_reset_rr(RESET_REG_MISC_INT_STATUS)
& ar71xx_reset_rr(RESET_REG_MISC_INT_ENABLE);
if (pending & MISC_INT_UART)
do_IRQ(AR71XX_MISC_IRQ_UART);
else if (pending & MISC_INT_DMA)
do_IRQ(AR71XX_MISC_IRQ_DMA);
else if (pending & MISC_INT_PERFC)
do_IRQ(AR71XX_MISC_IRQ_PERFC);
else if (pending & MISC_INT_TIMER)
do_IRQ(AR71XX_MISC_IRQ_TIMER);
else if (pending & MISC_INT_OHCI)
do_IRQ(AR71XX_MISC_IRQ_OHCI);
else if (pending & MISC_INT_ERROR)
do_IRQ(AR71XX_MISC_IRQ_ERROR);
else if (pending & MISC_INT_GPIO)
ar71xx_gpio_irq_dispatch();
else if (pending & MISC_INT_WDOG)
do_IRQ(AR71XX_MISC_IRQ_WDOG);
else
spurious_interrupt();
}
static void ar71xx_misc_irq_unmask(unsigned int irq)
{
irq -= AR71XX_MISC_IRQ_BASE;
ar71xx_reset_wr(RESET_REG_MISC_INT_ENABLE,
ar71xx_reset_rr(RESET_REG_MISC_INT_ENABLE) | (1 << irq));
}
static void ar71xx_misc_irq_mask(unsigned int irq)
{
irq -= AR71XX_MISC_IRQ_BASE;
ar71xx_reset_wr(RESET_REG_MISC_INT_ENABLE,
ar71xx_reset_rr(RESET_REG_MISC_INT_ENABLE) & ~(1 << irq));
}
struct irq_chip ar71xx_misc_irq_chip = {
.name = "AR71XX MISC",
.unmask = ar71xx_misc_irq_unmask,
.mask = ar71xx_misc_irq_mask,
.mask_ack = ar71xx_misc_irq_mask,
};
static struct irqaction ar71xx_misc_irqaction = {
.handler = no_action,
.name = "cascade [AR71XX MISC]",
};
static void __init ar71xx_misc_irq_init(void)
{
int i;
ar71xx_reset_wr(RESET_REG_MISC_INT_ENABLE, 0);
ar71xx_reset_wr(RESET_REG_MISC_INT_STATUS, 0);
for (i = AR71XX_MISC_IRQ_BASE;
i < AR71XX_MISC_IRQ_BASE + AR71XX_MISC_IRQ_COUNT; i++) {
irq_desc[i].status = IRQ_DISABLED;
set_irq_chip_and_handler(i, &ar71xx_misc_irq_chip,
handle_level_irq);
}
setup_irq(AR71XX_CPU_IRQ_MISC, &ar71xx_misc_irqaction);
}
asmlinkage void plat_irq_dispatch(void)
{
unsigned long pending;
pending = read_c0_status() & read_c0_cause() & ST0_IM;
if (pending & STATUSF_IP7)
do_IRQ(AR71XX_CPU_IRQ_TIMER);
#ifdef CONFIG_PCI
else if (pending & STATUSF_IP2)
ar71xx_pci_irq_dispatch();
#endif
else if (pending & STATUSF_IP4)
do_IRQ(AR71XX_CPU_IRQ_GE0);
else if (pending & STATUSF_IP5)
do_IRQ(AR71XX_CPU_IRQ_GE1);
else if (pending & STATUSF_IP3)
do_IRQ(AR71XX_CPU_IRQ_USB);
else if (pending & STATUSF_IP6)
ar71xx_misc_irq_dispatch();
else
spurious_interrupt();
}
void __init arch_init_irq(void)
{
mips_cpu_irq_init();
ar71xx_misc_irq_init();
#ifdef CONFIG_PCI
ar71xx_pci_irq_init();
#endif
ar71xx_gpio_irq_init();
}

View file

@ -0,0 +1,61 @@
/*
* Generic AR71xx machine setup
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <asm/mips_machine.h>
#include <asm/mach-ar71xx/ar71xx.h>
#include <asm/mach-ar71xx/pci.h>
#include <asm/mach-ar71xx/platform.h>
static struct spi_board_info ar71xx_generic_spi_info[] = {
{
.bus_num = 0,
.chip_select = 0,
.max_speed_hz = 25000000,
.modalias = "m25p80",
}
};
static struct ar71xx_pci_irq ar71xx_generic_pci_irqs[] __initdata = {
{
.slot = 0,
.pin = 1,
.irq = AR71XX_PCI_IRQ_DEV0,
}, {
.slot = 1,
.pin = 1,
.irq = AR71XX_PCI_IRQ_DEV1,
}, {
.slot = 2,
.pin = 1,
.irq = AR71XX_PCI_IRQ_DEV2,
}
};
static void __init ar71xx_generic_init(void)
{
ar71xx_add_device_spi(NULL, ar71xx_generic_spi_info,
ARRAY_SIZE(ar71xx_generic_spi_info));
ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x001f0000);
ar71xx_add_device_eth(1, PHY_INTERFACE_MODE_RMII, 0xffffffff);
ar71xx_add_device_usb();
ar71xx_pci_init(ARRAY_SIZE(ar71xx_generic_pci_irqs),
ar71xx_generic_pci_irqs);
}
MIPS_MACHINE(MACH_AR71XX_GENERIC, "Generic AR71xx board", ar71xx_generic_init);

View file

@ -0,0 +1,192 @@
/*
* MikroTik RouterBOARD 4xx series support
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/mmc/host.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/spi/mmc_spi.h>
#include <linux/leds.h>
#include <asm/mips_machine.h>
#include <asm/mach-ar71xx/ar71xx.h>
#include <asm/mach-ar71xx/pci.h>
#include <asm/mach-ar71xx/platform.h>
#define RB4XX_GPIO_USER_LED 4
static struct gpio_led rb4xx_leds_gpio[] = {
{
.name = "rb4xx:yellow:user",
.gpio = RB4XX_GPIO_USER_LED,
.active_low = 0,
},
};
static struct gpio_led_platform_data rb4xx_leds_gpio_data = {
.leds = rb4xx_leds_gpio,
.num_leds = ARRAY_SIZE(rb4xx_leds_gpio),
};
static struct platform_device rb4xx_leds_gpio_device = {
.name = "leds-gpio",
.id = -1,
.dev = {
.platform_data = &rb4xx_leds_gpio_data,
}
};
static struct platform_device rb4xx_nand_device = {
.name = "rb4xx-nand",
.id = -1,
};
static struct ar71xx_pci_irq rb4xx_pci_irqs[] __initdata = {
{
.slot = 1,
.pin = 1,
.irq = AR71XX_PCI_IRQ_DEV0,
}, {
.slot = 1,
.pin = 2,
.irq = AR71XX_PCI_IRQ_DEV1,
}, {
.slot = 2,
.pin = 1,
.irq = AR71XX_PCI_IRQ_DEV1,
}, {
.slot = 3,
.pin = 1,
.irq = AR71XX_PCI_IRQ_DEV2,
}
};
#if 0
/*
* SPI device support is experimental
*/
static struct flash_platform_data rb4xx_flash_data = {
.type = "pm25lv512",
};
static struct spi_board_info rb4xx_spi_info[] = {
{
.bus_num = 0,
.chip_select = 0,
.max_speed_hz = 25000000,
.modalias = "m25p80",
.platform_data = &rb4xx_flash_data,
}
};
static struct mmc_spi_platform_data rb433_mmc_data = {
.ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
};
static struct spi_board_info rb433_spi_info[] = {
{
.bus_num = 0,
.chip_select = 0,
.max_speed_hz = 25000000,
.modalias = "m25p80",
.platform_data = &rb433_flash_data,
}, {
.bus_num = 0,
.chip_select = 2,
.max_speed_hz = 25000000,
.modalias = "mmc_spi",
.platform_data = &rb433_mmc_data,
}
};
static u32 rb433_spi_get_ioc_base(u8 chip_select, int cs_high, int is_on)
{
u32 ret;
if (is_on == AR71XX_SPI_CS_INACTIVE) {
ret = SPI_IOC_CS0 | SPI_IOC_CS1;
} else {
if (cs_high) {
ret = SPI_IOC_CS0 | SPI_IOC_CS1;
} else {
if ((chip_select ^ 2) == 0)
ret = SPI_IOC_CS1 ^ (SPI_IOC_CS0 | SPI_IOC_CS1);
else
ret = SPI_IOC_CS0 ^ (SPI_IOC_CS0 | SPI_IOC_CS1);
}
}
return ret;
}
struct ar71xx_spi_platform_data rb433_spi_data = {
.bus_num = 0,
.num_chipselect = 3,
.get_ioc_base = rb433_spi_get_ioc_base,
};
static void rb4xx_add_device_spi(void)
{
ar71xx_add_device_spi(NULL, rb4xx_spi_info, ARRAY_SIZE(rb4xx_spi_info));
}
static void rb433_add_device_spi(void)
{
ar71xx_add_device_spi(&rb433_spi_data, rb433_spi_info,
ARRAY_SIZE(rb433_spi_info));
}
#else
static inline void rb4xx_add_device_spi(void) {}
static inline void rb433_add_device_spi(void) {}
#endif
static void __init rb411_setup(void)
{
rb4xx_add_device_spi();
ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x00000001);
platform_device_register(&rb4xx_leds_gpio_device);
platform_device_register(&rb4xx_nand_device);
ar71xx_pci_init(ARRAY_SIZE(rb4xx_pci_irqs), rb4xx_pci_irqs);
}
MIPS_MACHINE(MACH_AR71XX_RB_411, "MikroTik RouterBOARD 411/A/AH", rb411_setup);
static void __init rb433_setup(void)
{
rb433_add_device_spi();
ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x00000001);
ar71xx_add_device_eth(1, PHY_INTERFACE_MODE_RMII, 0xffffffff);
platform_device_register(&rb4xx_leds_gpio_device);
platform_device_register(&rb4xx_nand_device);
ar71xx_pci_init(ARRAY_SIZE(rb4xx_pci_irqs), rb4xx_pci_irqs);
}
MIPS_MACHINE(MACH_AR71XX_RB_433, "MikroTik RouterBOARD 433/AH", rb433_setup);
static void __init rb450_setup(void)
{
rb4xx_add_device_spi();
ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x00000001);
ar71xx_add_device_eth(1, PHY_INTERFACE_MODE_RMII, 0xffffffff);
platform_device_register(&rb4xx_leds_gpio_device);
platform_device_register(&rb4xx_nand_device);
}
MIPS_MACHINE(MACH_AR71XX_RB_450, "MikroTik RouterBOARD 450", rb450_setup);

View file

@ -0,0 +1,56 @@
/*
* Compex WP543 board support
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <asm/mips_machine.h>
#include <asm/mach-ar71xx/ar71xx.h>
#include <asm/mach-ar71xx/pci.h>
#include <asm/mach-ar71xx/platform.h>
static struct flash_platform_data wp543_flash_data = {
/* TODO: add partition map */
};
static struct spi_board_info wp543_spi_info[] = {
{
.bus_num = 0,
.chip_select = 0,
.max_speed_hz = 25000000,
.modalias = "m25p80",
.platform_data = &wp543_flash_data,
}
};
static struct ar71xx_pci_irq wp543_pci_irqs[] __initdata = {
{
.slot = 1,
.pin = 1,
.irq = AR71XX_PCI_IRQ_DEV0,
}, {
.slot = 1,
.pin = 2,
.irq = AR71XX_PCI_IRQ_DEV1,
}
};
static void __init wp543_setup(void)
{
ar71xx_add_device_spi(NULL, wp543_spi_info, ARRAY_SIZE(wp543_spi_info));
ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x00000001);
ar71xx_pci_init(ARRAY_SIZE(wp543_pci_irqs), wp543_pci_irqs);
}
MIPS_MACHINE(MACH_AR71XX_WP543, "Compex WP543", wp543_setup);

View file

@ -0,0 +1,301 @@
/*
* Atheros AR71xx SoC platform devices
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/serial_8250.h>
#include <asm/mips_machine.h>
#include <asm/mach-ar71xx/ar71xx.h>
#include <asm/mach-ar71xx/platform.h>
/*
* OHCI (USB full speed host controller)
*/
static struct resource ar71xx_usb_ohci_resources[] = {
[0] = {
.start = AR71XX_OHCI_BASE,
.end = AR71XX_OHCI_BASE + AR71XX_OHCI_SIZE - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = AR71XX_MISC_IRQ_OHCI,
.end = AR71XX_MISC_IRQ_OHCI,
.flags = IORESOURCE_IRQ,
},
};
static u64 ar71xx_ohci_dmamask = DMA_BIT_MASK(32);
static struct platform_device ar71xx_usb_ohci_device = {
.name = "ar71xx-ohci",
.id = 0,
.resource = ar71xx_usb_ohci_resources,
.num_resources = ARRAY_SIZE(ar71xx_usb_ohci_resources),
.dev = {
.dma_mask = &ar71xx_ohci_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
};
/*
* EHCI (USB full speed host controller)
*/
static struct resource ar71xx_usb_ehci_resources[] = {
[0] = {
.start = AR71XX_EHCI_BASE,
.end = AR71XX_EHCI_BASE + AR71XX_EHCI_SIZE - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = AR71XX_CPU_IRQ_USB,
.end = AR71XX_CPU_IRQ_USB,
.flags = IORESOURCE_IRQ,
},
};
static u64 ar71xx_ehci_dmamask = DMA_BIT_MASK(32);
static struct platform_device ar71xx_usb_ehci_device = {
.name = "ar71xx-ehci",
.id = 0,
.resource = ar71xx_usb_ehci_resources,
.num_resources = ARRAY_SIZE(ar71xx_usb_ehci_resources),
.dev = {
.dma_mask = &ar71xx_ehci_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
};
#define AR71XX_USB_RESET_MASK \
(RESET_MODULE_USB_HOST | RESET_MODULE_USB_PHY \
| RESET_MODULE_USB_OHCI_DLL)
void __init ar71xx_add_device_usb(void)
{
ar71xx_device_stop(AR71XX_USB_RESET_MASK);
mdelay(1000);
ar71xx_device_start(AR71XX_USB_RESET_MASK);
/* Turning on the Buff and Desc swap bits */
ar71xx_usb_ctrl_wr(USB_CTRL_REG_CONFIG, 0xf0000);
/* WAR for HW bug. Here it adjusts the duration between two SOFS */
ar71xx_usb_ctrl_wr(USB_CTRL_REG_FLADJ, 0x20c00);
mdelay(900);
platform_device_register(&ar71xx_usb_ohci_device);
platform_device_register(&ar71xx_usb_ehci_device);
}
#ifdef CONFIG_AR71XX_EARLY_SERIAL
static void __init ar71xx_add_device_uart(void) {};
#else
static struct resource ar71xx_uart_resources[] = {
{
.start = AR71XX_UART_BASE,
.end = AR71XX_UART_BASE + AR71XX_UART_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};
#define AR71XX_UART_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP)
static struct plat_serial8250_port ar71xx_uart_data[] = {
{
.mapbase = AR71XX_UART_BASE,
.irq = AR71XX_MISC_IRQ_UART,
.flags = AR71XX_UART_FLAGS,
.iotype = UPIO_MEM32,
.regshift = 2,
}, {
/* terminating entry */
}
};
static struct platform_device ar71xx_uart_device = {
.name = "serial8250",
.id = PLAT8250_DEV_PLATFORM,
.resource = ar71xx_uart_resources,
.num_resources = ARRAY_SIZE(ar71xx_uart_resources),
.dev = {
.platform_data = ar71xx_uart_data
},
};
static void __init ar71xx_add_device_uart(void)
{
ar71xx_uart_data[0].uartclk = ar71xx_ahb_freq;
platform_device_register(&ar71xx_uart_device);
}
#endif /* CONFIG_AR71XX_EARLY_SERIAL */
static struct resource ar71xx_eth0_resources[] = {
{
.name = "mac_base",
.flags = IORESOURCE_MEM,
.start = AR71XX_GE0_BASE,
.end = AR71XX_GE0_BASE + AR71XX_GE0_SIZE - 1,
}, {
.name = "mii_ctrl",
.flags = IORESOURCE_MEM,
.start = AR71XX_MII_BASE + MII_REG_MII0_CTRL,
.end = AR71XX_MII_BASE + MII_REG_MII0_CTRL + 3,
}, {
.name = "mac_irq",
.flags = IORESOURCE_IRQ,
.start = AR71XX_CPU_IRQ_GE0,
.end = AR71XX_CPU_IRQ_GE0,
},
};
static struct ag71xx_platform_data ar71xx_eth0_data = {
.reset_bit = RESET_MODULE_GE0_MAC,
.flush_reg = DDR_REG_FLUSH_GE0,
};
static struct platform_device ar71xx_eth0_device = {
.name = "ag71xx",
.id = 0,
.resource = ar71xx_eth0_resources,
.num_resources = ARRAY_SIZE(ar71xx_eth0_resources),
.dev = {
.platform_data = &ar71xx_eth0_data,
},
};
static struct resource ar71xx_eth1_resources[] = {
{
.name = "mac_base",
.flags = IORESOURCE_MEM,
.start = AR71XX_GE1_BASE,
.end = AR71XX_GE1_BASE + AR71XX_GE1_SIZE - 1,
}, {
.name = "mii_ctrl",
.flags = IORESOURCE_MEM,
.start = AR71XX_MII_BASE + MII_REG_MII1_CTRL,
.end = AR71XX_MII_BASE + MII_REG_MII1_CTRL + 3,
}, {
.name = "mac_irq",
.flags = IORESOURCE_IRQ,
.start = AR71XX_CPU_IRQ_GE1,
.end = AR71XX_CPU_IRQ_GE1,
},
};
static struct ag71xx_platform_data ar71xx_eth1_data = {
.reset_bit = RESET_MODULE_GE1_MAC,
.flush_reg = DDR_REG_FLUSH_GE1,
};
static struct platform_device ar71xx_eth1_device = {
.name = "ag71xx",
.id = 1,
.resource = ar71xx_eth1_resources,
.num_resources = ARRAY_SIZE(ar71xx_eth1_resources),
.dev = {
.platform_data = &ar71xx_eth1_data,
},
};
void __init ar71xx_add_device_eth(unsigned int id, phy_interface_t phy_if_mode,
u32 phy_mask)
{
struct platform_device *pdev;
switch (id) {
case 0:
switch (phy_if_mode) {
case PHY_INTERFACE_MODE_MII:
ar71xx_eth0_data.mii_if = MII0_CTRL_IF_MII;
break;
case PHY_INTERFACE_MODE_GMII:
ar71xx_eth0_data.mii_if = MII0_CTRL_IF_GMII;
break;
case PHY_INTERFACE_MODE_RGMII:
ar71xx_eth0_data.mii_if = MII0_CTRL_IF_RGMII;
break;
case PHY_INTERFACE_MODE_RMII:
ar71xx_eth0_data.mii_if = MII0_CTRL_IF_RMII;
break;
default:
BUG();
}
ar71xx_eth0_data.phy_if_mode = phy_if_mode;
ar71xx_eth0_data.phy_mask = phy_mask;
pdev = &ar71xx_eth0_device;
break;
case 1:
switch (phy_if_mode) {
case PHY_INTERFACE_MODE_RMII:
ar71xx_eth1_data.mii_if = MII1_CTRL_IF_RMII;
break;
case PHY_INTERFACE_MODE_RGMII:
ar71xx_eth1_data.mii_if = MII1_CTRL_IF_RGMII;
break;
default:
BUG();
}
ar71xx_eth1_data.phy_if_mode = phy_if_mode;
ar71xx_eth1_data.phy_mask = phy_mask;
pdev = &ar71xx_eth1_device;
break;
default:
pdev = NULL;
break;
}
if (pdev)
platform_device_register(pdev);
}
static struct resource ar71xx_spi_resources[] = {
[0] = {
.start = AR71XX_SPI_BASE,
.end = AR71XX_SPI_BASE + AR71XX_SPI_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device ar71xx_spi_device = {
.name = "ar71xx-spi",
.id = -1,
.resource = ar71xx_spi_resources,
.num_resources = ARRAY_SIZE(ar71xx_spi_resources),
};
void __init ar71xx_add_device_spi(struct ar71xx_spi_platform_data *pdata,
struct spi_board_info const *info,
unsigned n)
{
ar71xx_gpio_function_enable(GPIO_FUNC_SPI_EN);
spi_register_board_info(info, n);
ar71xx_spi_device.dev.platform_data = pdata;
platform_device_register(&ar71xx_spi_device);
}
static int __init ar71xx_machine_setup(void)
{
ar71xx_print_cmdline();
ar71xx_gpio_init();
ar71xx_add_device_uart();
mips_machine_setup();
return 0;
}
arch_initcall(ar71xx_machine_setup);

View file

@ -0,0 +1,126 @@
/*
* Atheros AR71xx SoC specific prom routines
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/serial_reg.h>
#include <asm/bootinfo.h>
#include <asm/addrspace.h>
#include <asm/mach-ar71xx/ar71xx.h>
struct board_rec {
char *name;
unsigned long mach_type;
};
static int prom_argc __initdata;
static char **prom_argv __initdata;
static char **prom_envp __initdata;
static struct board_rec boards[] __initdata = {
{
.name = "411",
.mach_type = MACH_AR71XX_RB_411,
}, {
.name = "433",
.mach_type = MACH_AR71XX_RB_433,
}, {
.name = "450",
.mach_type = MACH_AR71XX_RB_450,
}
};
char *(*prom_getenv)(const char *envname) __initdata;
static __init char *dummy_getenv(const char *envname)
{
return NULL;
}
static void __init routerboot_printargs(void)
{
int i;
for (i = 0; i < prom_argc; i++)
printk(KERN_DEBUG "prom: routerboot envp[%d]: %s\n",
i, prom_envp[i]);
}
static __init char *routerboot_getenv(const char *envname)
{
char **env;
int i = strlen(envname);
for (env = prom_envp; *env != NULL; env++)
if (strncmp(envname, *env, i) == 0 && (*env)[i] == '=')
return *env + i + 1;
return NULL;
}
static __init unsigned long find_board_byname(char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(boards); i++)
if (strcmp(name, boards[i].name) == 0)
return boards[i].mach_type;
return MACH_AR71XX_GENERIC;
}
void __init prom_init(void)
{
char *board;
printk(KERN_DEBUG "prom: fw_arg0=%08x, fw_arg1=%08x, "
"fw_arg2=%08x, fw_arg3=%08x\n",
(unsigned int)fw_arg0, (unsigned int)fw_arg1,
(unsigned int)fw_arg2, (unsigned int)fw_arg3);
prom_getenv = dummy_getenv;
if ((fw_arg0 == 7) && (fw_arg2 == 0)) {
prom_argc = fw_arg0;
prom_envp = (char **)fw_arg1;
prom_getenv = routerboot_getenv;
routerboot_printargs();
}
board = prom_getenv("board");
if (board)
mips_machtype = find_board_byname(board);
else
mips_machtype = MACH_AR71XX_GENERIC;
ar71xx_print_cmdline();
}
void __init prom_free_prom_memory(void)
{
/* We do not have to prom memory to free */
}
#define UART_READ(r) \
__raw_readl((void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE) + 4 * (r)))
#define UART_WRITE(r, v) \
__raw_writel((v), (void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE) + 4*(r)))
void prom_putchar(unsigned char ch)
{
while (((UART_READ(UART_LSR)) & UART_LSR_THRE) == 0);
UART_WRITE(UART_TX, ch);
while (((UART_READ(UART_LSR)) & UART_LSR_THRE) == 0);
}

View file

@ -0,0 +1,226 @@
/*
* Atheros AR71xx SoC specific setup
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/serial_8250.h>
#include <linux/bootmem.h>
#include <asm/bootinfo.h>
#include <asm/traps.h>
#include <asm/time.h> /* for mips_hpt_frequency */
#include <asm/reboot.h> /* for _machine_{restart,halt} */
#include <asm/mach-ar71xx/ar71xx.h>
#include <asm/mach-ar71xx/pci.h>
#include <asm/mach-ar71xx/platform.h>
#define AR71XX_SYS_TYPE_LEN 64
#define AR71XX_BASE_FREQ 40000000
#define AR71XX_MEM_SIZE_MIN 0x0200000
#define AR71XX_MEM_SIZE_MAX 0x8000000
u32 ar71xx_cpu_freq;
EXPORT_SYMBOL_GPL(ar71xx_cpu_freq);
u32 ar71xx_ahb_freq;
EXPORT_SYMBOL_GPL(ar71xx_ahb_freq);
u32 ar71xx_ddr_freq;
EXPORT_SYMBOL_GPL(ar71xx_ddr_freq);
int (*ar71xx_pci_bios_init)(unsigned nr_irqs,
struct ar71xx_pci_irq *map) __initdata;
int (*ar71xx_pci_be_handler)(int is_fixup);
static char ar71xx_sys_type[AR71XX_SYS_TYPE_LEN];
static void ar71xx_restart(char *command)
{
ar71xx_device_stop(RESET_MODULE_FULL_CHIP);
for (;;)
if (cpu_wait)
cpu_wait();
}
static void ar71xx_halt(void)
{
while (1)
cpu_wait();
}
static int ar71xx_be_handler(struct pt_regs *regs, int is_fixup)
{
int err = 0;
if (ar71xx_pci_be_handler)
err = ar71xx_pci_be_handler(is_fixup);
return (is_fixup && !err) ? MIPS_BE_FIXUP : MIPS_BE_FATAL;
}
int __init ar71xx_pci_init(unsigned nr_irqs, struct ar71xx_pci_irq *map)
{
if (!ar71xx_pci_bios_init)
return 0;
return ar71xx_pci_bios_init(nr_irqs, map);
}
static void __init ar71xx_detect_mem_size(void)
{
volatile u8 *p;
u8 memsave;
u32 size;
p = (volatile u8 *) KSEG1ADDR(0);
memsave = *p;
for (size = AR71XX_MEM_SIZE_MIN;
size <= (AR71XX_MEM_SIZE_MAX >> 1); size <<= 1) {
volatile u8 *r;
r = (p + size);
*p = 0x55;
if (*r == 0x55) {
/* Mirrored data found, try another pattern */
*p = 0xAA;
if (*r == 0xAA) {
/* Mirrored data found again, stop detection */
break;
}
}
}
*p = memsave;
add_memory_region(0, size, BOOT_MEM_RAM);
}
static void __init ar71xx_detect_sys_type(void)
{
char *chip;
u32 id;
u32 rev;
id = ar71xx_reset_rr(RESET_REG_REV_ID) & REV_ID_MASK;
rev = (id >> REV_ID_REVISION_SHIFT) & REV_ID_REVISION_MASK;
switch (id & REV_ID_CHIP_MASK) {
case REV_ID_CHIP_AR7130:
chip = "7130";
break;
case REV_ID_CHIP_AR7141:
chip = "7141";
break;
case REV_ID_CHIP_AR7161:
chip = "7161";
break;
default:
chip = "71xx";
}
sprintf(ar71xx_sys_type, "Atheros AR%s rev %u (id:0x%02x)",
chip, rev, id);
}
static void __init ar71xx_detect_sys_frequency(void)
{
u32 pll;
u32 freq;
u32 div;
pll = ar71xx_pll_rr(PLL_REG_CPU_PLL_CFG);
div = ((pll >> PLL_DIV_SHIFT) & PLL_DIV_MASK) + 1;
freq = div * AR71XX_BASE_FREQ;
div = ((pll >> CPU_DIV_SHIFT) & CPU_DIV_MASK) + 1;
ar71xx_cpu_freq = freq / div;
div = ((pll >> DDR_DIV_SHIFT) & DDR_DIV_MASK) + 1;
ar71xx_ddr_freq = freq / div;
div = (((pll >> AHB_DIV_SHIFT) & AHB_DIV_MASK) + 1) * 2;
ar71xx_ahb_freq = ar71xx_cpu_freq / div;
}
#ifdef CONFIG_AR71XX_EARLY_SERIAL
static void __init ar71xx_early_serial_setup(void)
{
struct uart_port p;
memset(&p, 0, sizeof(p));
p.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP;
p.iotype = UPIO_MEM32;
p.uartclk = ar71xx_ahb_freq;
p.irq = AR71XX_MISC_IRQ_UART;
p.regshift = 2;
p.mapbase = AR71XX_UART_BASE;
early_serial_setup(&p);
}
#else
static inline void ar71xx_early_serial_setup(void) {};
#endif /* CONFIG_AR71XX_EARLY_SERIAL */
const char *get_system_type(void)
{
return ar71xx_sys_type;
}
unsigned int __cpuinit get_c0_compare_irq(void)
{
return CP0_LEGACY_COMPARE_IRQ;
}
void __init plat_mem_setup(void)
{
set_io_port_base(KSEG1);
ar71xx_ddr_base = ioremap_nocache(AR71XX_DDR_CTRL_BASE,
AR71XX_DDR_CTRL_SIZE);
ar71xx_pll_base = ioremap_nocache(AR71XX_PLL_BASE,
AR71XX_PLL_SIZE);
ar71xx_reset_base = ioremap_nocache(AR71XX_RESET_BASE,
AR71XX_RESET_SIZE);
ar71xx_gpio_base = ioremap_nocache(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE);
ar71xx_usb_ctrl_base = ioremap_nocache(AR71XX_USB_CTRL_BASE,
AR71XX_USB_CTRL_SIZE);
ar71xx_detect_mem_size();
ar71xx_detect_sys_type();
ar71xx_detect_sys_frequency();
_machine_restart = ar71xx_restart;
_machine_halt = ar71xx_halt;
pm_power_off = ar71xx_halt;
board_be_handler = ar71xx_be_handler;
ar71xx_print_cmdline();
ar71xx_early_serial_setup();
}
void __init plat_time_init(void)
{
mips_hpt_frequency = ar71xx_cpu_freq / 2;
}

View file

@ -0,0 +1,346 @@
/*
* Atheros AR71xx PCI host controller driver
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/resource.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/pci.h>
#include <linux/pci_regs.h>
#include <asm/mach-ar71xx/ar71xx.h>
#include <asm/mach-ar71xx/pci.h>
#undef DEBUG
#ifdef DEBUG
#define DBG(fmt, args...) printk(KERN_DEBUG fmt, ## args)
#else
#define DBG(fmt, args...)
#endif
#define AR71XX_PCI_DELAY 100 /* msecs */
#if 0
#define PCI_IDSEL_BASE PCI_IDSEL_ADL_START
#else
#define PCI_IDSEL_BASE 0
#endif
static unsigned ar71xx_pci_nr_irqs;
static struct ar71xx_pci_irq *ar71xx_pci_irq_map __initdata;
static void __iomem *ar71xx_pcicfg_base;
static DEFINE_SPINLOCK(ar71xx_pci_lock);
static inline void ar71xx_pci_delay(void)
{
mdelay(AR71XX_PCI_DELAY);
}
static inline u32 ar71xx_pcicfg_rr(unsigned int reg)
{
return __raw_readl(ar71xx_pcicfg_base + reg);
}
static inline void ar71xx_pcicfg_wr(unsigned int reg, u32 val)
{
__raw_writel(val, ar71xx_pcicfg_base + reg);
}
/* Byte lane enable bits */
static u8 ble_table[4][4] = {
{0xf, 0xe, 0xd, 0xc},
{0xc, 0x9, 0x3, 0x1},
{0x0, 0x0, 0x0, 0x0},
{0x0, 0x0, 0x0, 0x0},
};
static inline u32 ar71xx_pci_get_ble(int where, int size, int local)
{
u32 t;
t = ble_table[size][where & 3];
t <<= (local) ? 20 : 4;
return t;
}
static inline u32 ar71xx_pci_bus_addr(struct pci_bus *bus, unsigned int devfn,
int where)
{
u32 ret;
if (!bus->number) {
/* type 0 */
ret = (1 << (PCI_IDSEL_BASE + PCI_SLOT(devfn)))
| (PCI_FUNC(devfn) << 8) | (where & ~3);
} else {
/* type 1 */
ret = (bus->number << 16) | (PCI_SLOT(devfn) << 11)
| (PCI_FUNC(devfn) << 8) | (where & ~3) | 1;
}
return ret;
}
static int __ar71xx_pci_be_handler(int is_fixup)
{
u32 pci_err;
u32 ahb_err;
pci_err = ar71xx_pcicfg_rr(PCI_REG_PCI_ERR) & 3;
if (pci_err) {
if (!is_fixup)
printk(KERN_ALERT "PCI error %d at PCI addr 0x%x\n",
pci_err,
ar71xx_pcicfg_rr(PCI_REG_PCI_ERR_ADDR));
ar71xx_pcicfg_wr(PCI_REG_PCI_ERR, pci_err);
}
ahb_err = ar71xx_pcicfg_rr(PCI_REG_AHB_ERR) & 1;
if (ahb_err) {
if (!is_fixup)
printk(KERN_ALERT "AHB error at AHB address 0x%x\n",
ar71xx_pcicfg_rr(PCI_REG_AHB_ERR_ADDR));
ar71xx_pcicfg_wr(PCI_REG_AHB_ERR, ahb_err);
}
return ((ahb_err | pci_err) ? 1 : 0);
}
static inline int ar71xx_pci_set_cfgaddr(struct pci_bus *bus,
unsigned int devfn, int where, int size, u32 cmd)
{
u32 addr;
addr = ar71xx_pci_bus_addr(bus, devfn, where);
DBG("PCI: set cfgaddr: %02x:%02x.%01x/%02x:%01d, addr=%08x\n",
bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn),
where, size, addr);
ar71xx_pcicfg_wr(PCI_REG_CFG_AD, addr);
ar71xx_pcicfg_wr(PCI_REG_CFG_CBE,
cmd | ar71xx_pci_get_ble(where, size, 0));
return __ar71xx_pci_be_handler(1);
}
static int ar71xx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *value)
{
static u32 mask[8] = {0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0};
unsigned long flags;
u32 data;
int ret;
ret = PCIBIOS_SUCCESSFUL;
DBG("PCI: read config: %02x:%02x.%01x/%02x:%01d\n", bus->number,
PCI_SLOT(devfn), PCI_FUNC(devfn), where, size);
spin_lock_irqsave(&ar71xx_pci_lock, flags);
if (bus->number == 0 && devfn == 0) {
u32 t;
t = PCI_CRP_CMD_READ | (where & ~3);
ar71xx_pcicfg_wr(PCI_REG_CRP_AD_CBE, t);
data = ar71xx_pcicfg_rr(PCI_REG_CRP_RDDATA);
DBG("PCI: rd local cfg, ad_cbe:%08x, data:%08x\n", t, data);
} else {
int err;
err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size,
PCI_CFG_CMD_READ);
if (err == 0) {
data = ar71xx_pcicfg_rr(PCI_REG_CFG_RDDATA);
} else {
ret = PCIBIOS_DEVICE_NOT_FOUND;
data = ~0;
}
}
spin_unlock_irqrestore(&ar71xx_pci_lock, flags);
DBG("PCI: read config: data=%08x raw=%08x\n",
(data >> (8 * (where & 3))) & mask[size & 7], data);
*value = (data >> (8 * (where & 3))) & mask[size & 7];
return ret;
}
static int ar71xx_pci_write_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 value)
{
unsigned long flags;
int ret;
DBG("PCI: write config: %02x:%02x.%01x/%02x:%01d value=%08x\n",
bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn),
where, size, value);
value = value << (8 * (where & 3));
ret = PCIBIOS_SUCCESSFUL;
spin_lock_irqsave(&ar71xx_pci_lock, flags);
if (bus->number == 0 && devfn == 0) {
u32 t;
t = PCI_CRP_CMD_WRITE | (where & ~3);
t |= ar71xx_pci_get_ble(where, size, 1);
DBG("PCI: wr local cfg, ad_cbe:%08x, value:%08x\n", t, value);
ar71xx_pcicfg_wr(PCI_REG_CRP_AD_CBE, t);
ar71xx_pcicfg_wr(PCI_REG_CRP_WRDATA, value);
} else {
int err;
err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size,
PCI_CFG_CMD_WRITE);
if (err == 0)
ar71xx_pcicfg_wr(PCI_REG_CFG_WRDATA, value);
else
ret = PCIBIOS_DEVICE_NOT_FOUND;
}
spin_unlock_irqrestore(&ar71xx_pci_lock, flags);
return ret;
}
static void ar71xx_pci_fixup(struct pci_dev *dev)
{
u32 t;
if (dev->bus->number != 0 || dev->devfn != 0)
return;
DBG("PCI: fixup host controller %s (%04x:%04x)\n", pci_name(dev),
dev->vendor, dev->device);
/* setup COMMAND register */
t = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
| PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK;
pci_write_config_word(dev, PCI_COMMAND, t);
}
DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ar71xx_pci_fixup);
int __init pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin)
{
int irq = -1;
int i;
slot -= PCI_IDSEL_ADL_START - PCI_IDSEL_BASE;
for (i = 0; i < ar71xx_pci_nr_irqs; i++) {
struct ar71xx_pci_irq *entry;
entry = &ar71xx_pci_irq_map[i];
if (entry->slot == slot && entry->pin == pin) {
irq = entry->irq;
break;
}
}
if (irq < 0) {
printk(KERN_ALERT "PCI: no irq found for pin%u@%s\n",
pin, pci_name((struct pci_dev *)dev));
} else {
printk(KERN_INFO "PCI: mapping irq %d to pin%u@%s\n",
irq, pin, pci_name((struct pci_dev *)dev));
}
return irq;
}
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}
static struct pci_ops ar71xx_pci_ops = {
.read = ar71xx_pci_read_config,
.write = ar71xx_pci_write_config,
};
static struct resource ar71xx_pci_io_resource = {
.name = "PCI IO space",
.start = 0,
.end = 0,
.flags = IORESOURCE_IO,
};
static struct resource ar71xx_pci_mem_resource = {
.name = "PCI memory space",
.start = AR71XX_PCI_MEM_BASE,
.end = AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1,
.flags = IORESOURCE_MEM
};
static struct pci_controller ar71xx_pci_controller = {
.pci_ops = &ar71xx_pci_ops,
.mem_resource = &ar71xx_pci_mem_resource,
.io_resource = &ar71xx_pci_io_resource,
};
static int __init __ar71xx_pci_bios_init(unsigned nr_irqs,
struct ar71xx_pci_irq *map)
{
ar71xx_device_stop(RESET_MODULE_PCI_BUS | RESET_MODULE_PCI_CORE);
ar71xx_pci_delay();
ar71xx_device_start(RESET_MODULE_PCI_BUS | RESET_MODULE_PCI_CORE);
ar71xx_pci_delay();
ar71xx_pcicfg_base = ioremap_nocache(AR71XX_PCI_CFG_BASE,
AR71XX_PCI_CFG_SIZE);
ar71xx_ddr_wr(DDR_REG_PCI_WIN0, PCI_WIN0_OFFS);
ar71xx_ddr_wr(DDR_REG_PCI_WIN1, PCI_WIN1_OFFS);
ar71xx_ddr_wr(DDR_REG_PCI_WIN2, PCI_WIN2_OFFS);
ar71xx_ddr_wr(DDR_REG_PCI_WIN3, PCI_WIN3_OFFS);
ar71xx_ddr_wr(DDR_REG_PCI_WIN4, PCI_WIN4_OFFS);
ar71xx_ddr_wr(DDR_REG_PCI_WIN5, PCI_WIN5_OFFS);
ar71xx_ddr_wr(DDR_REG_PCI_WIN6, PCI_WIN6_OFFS);
ar71xx_ddr_wr(DDR_REG_PCI_WIN7, PCI_WIN7_OFFS);
ar71xx_pci_delay();
/* clear bus errors */
(void)__ar71xx_pci_be_handler(1);
ar71xx_pci_nr_irqs = nr_irqs;
ar71xx_pci_irq_map = map;
ar71xx_pci_be_handler = __ar71xx_pci_be_handler;
register_pci_controller(&ar71xx_pci_controller);
return 0;
}
static int __init __ar71xx_pci_init(void)
{
ar71xx_pci_bios_init = __ar71xx_pci_bios_init;
return 0;
}
pure_initcall(__ar71xx_pci_init);

View file

@ -0,0 +1,507 @@
/*
* NAND flash driver for the MikroTik RouterBoard 4xx series
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This file was based on the driver for Linux 2.6.22 published by
* MikroTik for their RouterBoard 4xx series devices.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <asm/mach-ar71xx/ar71xx.h>
#define DRV_NAME "rb4xx-nand"
#define DRV_VERSION "0.1.9"
#define DRV_DESC "NAND flash driver for RouterBoard 4xx series"
#define USE_FAST_READ 1
#define USE_FAST_WRITE 1
#undef RB4XX_NAND_DEBUG
#ifdef RB4XX_NAND_DEBUG
#define DBG(fmt, arg...) printk(KERN_DEBUG DRV_NAME ": " fmt, ## arg)
#else
#define DBG(fmt, arg...) do {} while (0)
#endif
#define RB4XX_NAND_GPIO_RDY 5
#define RB4XX_FLASH_HZ 33333334
#define RB4XX_NAND_HZ 33333334
#define SPI_CTRL_FASTEST 0x40
#define SPI_CTRL_SAFE 0x43 /* 25 MHz for AHB 200 MHz */
#define SBIT_IOC_BASE SPI_IOC_CS1
#define SBIT_IOC_DO_SHIFT 0
#define SBIT_IOC_DO (1u << SBIT_IOC_DO_SHIFT)
#define SBIT_IOC_DO2_SHIFT 18
#define SBIT_IOC_DO2 (1u << SBIT_IOC_DO2_SHIFT)
#define CPLD_CMD_WRITE_MULT 0x08 /* send cmd, n x send data, read data */
#define CPLD_CMD_WRITE_CFG 0x09 /* send cmd, n x send cfg */
#define CPLD_CMD_READ_MULT 0x0a /* send cmd, send idle, n x read data */
#define CPLD_CMD_READ_FAST 0x0b /* send cmd, 4 x idle, n x read data */
#define CFG_BIT_nCE 0x80
#define CFG_BIT_CLE 0x40
#define CFG_BIT_ALE 0x20
#define CFG_BIT_FAN 0x10
#define CFG_BIT_nLED4 0x08
#define CFG_BIT_nLED3 0x04
#define CFG_BIT_nLED2 0x02
#define CFG_BIT_nLED1 0x01
#define CFG_BIT_nLEDS \
(CFG_BIT_nLED1 | CFG_BIT_nLED2 | CFG_BIT_nLED3 | CFG_BIT_nLED4)
struct rb4xx_nand_info {
struct nand_chip chip;
struct mtd_info mtd;
};
/*
* We need to use the OLD Yaffs-1 OOB layout, otherwise the RB bootloader
* will not be able to find the kernel that we load.
*/
static struct nand_ecclayout rb4xx_nand_ecclayout = {
.eccbytes = 6,
.eccpos = { 8, 9, 10, 13, 14, 15 },
.oobavail = 9,
.oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } }
};
static struct mtd_partition rb4xx_nand_partitions[] = {
{
.name = "kernel",
.offset = (256 * 1024),
.size = (4 * 1024 * 1024) - (256 * 1024),
},
{
.name = "rootfs",
.offset = MTDPART_OFS_NXTBLK,
.size = MTDPART_SIZ_FULL,
},
{
.name = "booter",
.offset = 0,
.size = (256 * 1024),
.mask_flags = MTD_WRITEABLE,
}
};
#if USE_FAST_READ
#define SPI_NDATA_BASE 0x00800000
static unsigned spi_ctrl_fread = SPI_CTRL_SAFE;
static unsigned spi_ctrl_flash = SPI_CTRL_SAFE;
extern unsigned mips_hpt_frequency;
#endif
static inline unsigned rb4xx_spi_rreg(unsigned r)
{
return __raw_readl((void * __iomem)(KSEG1ADDR(AR71XX_SPI_BASE) + r));
}
static inline void rb4xx_spi_wreg(unsigned r, unsigned v)
{
__raw_writel(v, (void * __iomem)(KSEG1ADDR(AR71XX_SPI_BASE) + r));
}
static inline void do_spi_clk(int bit)
{
unsigned bval = SBIT_IOC_BASE | (bit & 1);
rb4xx_spi_wreg(SPI_REG_IOC, bval);
rb4xx_spi_wreg(SPI_REG_IOC, bval | SPI_IOC_CLK);
}
static void do_spi_byte(uint8_t byte)
{
do_spi_clk(byte >> 7);
do_spi_clk(byte >> 6);
do_spi_clk(byte >> 5);
do_spi_clk(byte >> 4);
do_spi_clk(byte >> 3);
do_spi_clk(byte >> 2);
do_spi_clk(byte >> 1);
do_spi_clk(byte);
DBG("spi_byte sent 0x%02x got 0x%x\n",
byte, rb4xx_spi_rreg(SPI_REG_RDS));
}
#if USE_FAST_WRITE
static inline void do_spi_clk_fast(int bit1, int bit2)
{
unsigned bval = (SBIT_IOC_BASE |
((bit1 << SBIT_IOC_DO_SHIFT) & SBIT_IOC_DO) |
((bit2 << SBIT_IOC_DO2_SHIFT) & SBIT_IOC_DO2));
rb4xx_spi_wreg(SPI_REG_IOC, bval);
rb4xx_spi_wreg(SPI_REG_IOC, bval | SPI_IOC_CLK);
}
static inline void do_spi_byte_fast(uint8_t byte)
{
do_spi_clk_fast(byte >> 7, byte >> 6);
do_spi_clk_fast(byte >> 5, byte >> 4);
do_spi_clk_fast(byte >> 3, byte >> 2);
do_spi_clk_fast(byte >> 1, byte >> 0);
DBG("spi_byte_fast sent 0x%02x got 0x%x\n",
byte, rb4xx_spi_rreg(SPI_REG_RDS));
}
#else
static inline void do_spi_byte_fast(uint8_t byte)
{
do_spi_byte(byte);
}
#endif /* USE_FAST_WRITE */
static int do_spi_cmd(unsigned cmd, unsigned sendCnt, const uint8_t *sendData,
unsigned recvCnt, uint8_t *recvData,
const uint8_t *verifyData, int fastWrite)
{
unsigned i;
DBG("SPI cmd 0x%x send %u recv %u\n", cmd, sendCnt, recvCnt);
rb4xx_spi_wreg(SPI_REG_FS, SPI_FS_GPIO);
rb4xx_spi_wreg(SPI_REG_CTRL, SPI_CTRL_FASTEST);
do_spi_byte(cmd);
#if 0
if (cmd == CPLD_CMD_READ_FAST) {
do_spi_byte(0x80);
do_spi_byte(0);
do_spi_byte(0);
}
#endif
for (i = 0; i < sendCnt; ++i) {
if (fastWrite)
do_spi_byte_fast(sendData[i]);
else
do_spi_byte(sendData[i]);
}
for (i = 0; i < recvCnt; ++i) {
if (fastWrite)
do_spi_byte_fast(0);
else
do_spi_byte(0);
if (recvData) {
recvData[i] = rb4xx_spi_rreg(SPI_REG_RDS) & 0xff;
} else if (verifyData) {
if (verifyData[i] != (rb4xx_spi_rreg(SPI_REG_RDS)
& 0xff))
break;
}
}
rb4xx_spi_wreg(SPI_REG_IOC, SBIT_IOC_BASE | SPI_IOC_CS0);
rb4xx_spi_wreg(SPI_REG_CTRL, spi_ctrl_flash);
rb4xx_spi_wreg(SPI_REG_FS, 0);
return i == recvCnt;
}
static int got_write = 1;
static void rb4xx_nand_write_data(const uint8_t *byte, unsigned cnt)
{
do_spi_cmd(CPLD_CMD_WRITE_MULT, cnt, byte, 1, NULL, NULL, 1);
got_write = 1;
}
static void rb4xx_nand_write_byte(uint8_t byte)
{
rb4xx_nand_write_data(&byte, 1);
}
#if USE_FAST_READ
static uint8_t *rb4xx_nand_read_getaddr(unsigned cnt)
{
static unsigned nboffset = 0x100000;
unsigned addr;
if (got_write) {
nboffset = (nboffset + 31) & ~31;
if (nboffset >= 0x100000) /* 1MB */
nboffset = 0;
got_write = 0;
rb4xx_spi_wreg(SPI_REG_FS, SPI_FS_GPIO);
rb4xx_spi_wreg(SPI_REG_CTRL, spi_ctrl_fread);
rb4xx_spi_wreg(SPI_REG_FS, 0);
}
addr = KSEG1ADDR(AR71XX_SPI_BASE + SPI_NDATA_BASE) + nboffset;
DBG("rb4xx_nand_read_getaddr 0x%x cnt 0x%x\n", addr, cnt);
nboffset += cnt;
return (uint8_t *)addr;
}
static void rb4xx_nand_read_data(uint8_t *buf, unsigned cnt)
{
unsigned size32 = cnt & ~31;
unsigned remain = cnt & 31;
if (size32) {
uint8_t *addr = rb4xx_nand_read_getaddr(size32);
memcpy(buf, (void *)addr, size32);
}
if (remain) {
do_spi_cmd(CPLD_CMD_READ_MULT, 1, buf, remain,
buf + size32, NULL, 0);
}
}
static int rb4xx_nand_verify_data(const uint8_t *buf, unsigned cnt)
{
unsigned size32 = cnt & ~31;
unsigned remain = cnt & 31;
if (size32) {
uint8_t *addr = rb4xx_nand_read_getaddr(size32);
if (memcmp(buf, (void *)addr, size32) != 0)
return 0;
}
if (remain) {
return do_spi_cmd(CPLD_CMD_READ_MULT, 1, buf, remain,
NULL, buf + size32, 0);
}
return 1;
}
#else /* USE_FAST_READ */
static void rb4xx_nand_read_data(uint8_t *buf, unsigned cnt)
{
do_spi_cmd(CPLD_CMD_READ_MULT, 1, buf, cnt, buf, NULL, 0);
}
static int rb4xx_nand_verify_data(const uint8_t *buf, unsigned cnt)
{
return do_spi_cmd(CPLD_CMD_READ_MULT, 1, buf, cnt, NULL, buf, 0);
}
#endif /* USE_FAST_READ */
static void rb4xx_nand_write_cfg(uint8_t byte)
{
do_spi_cmd(CPLD_CMD_WRITE_CFG, 1, &byte, 0, NULL, NULL, 0);
got_write = 1;
}
static int rb4xx_nand_dev_ready(struct mtd_info *mtd)
{
return gpio_get_value(RB4XX_NAND_GPIO_RDY);
}
static void rb4xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
if (ctrl & NAND_CTRL_CHANGE) {
uint8_t cfg = CFG_BIT_nLEDS;
cfg |= (ctrl & NAND_CLE) ? CFG_BIT_CLE : 0;
cfg |= (ctrl & NAND_ALE) ? CFG_BIT_ALE : 0;
cfg |= (ctrl & NAND_NCE) ? 0 : CFG_BIT_nCE;
rb4xx_nand_write_cfg(cfg);
}
if (cmd != NAND_CMD_NONE)
rb4xx_nand_write_byte(cmd);
}
static uint8_t rb4xx_nand_read_byte(struct mtd_info *mtd)
{
uint8_t byte = 0;
rb4xx_nand_read_data(&byte, 1);
return byte;
}
static void rb4xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
rb4xx_nand_write_data(buf, len);
}
static void rb4xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf,
int len)
{
rb4xx_nand_read_data(buf, len);
}
static int rb4xx_nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
if (!rb4xx_nand_verify_data(buf, len))
return -EFAULT;
return 0;
}
static unsigned get_spi_ctrl(unsigned hz_max, const char *name)
{
unsigned div;
div = (ar71xx_ahb_freq - 1) / (2 * hz_max);
/*
* CPU has a bug at (div == 0) - first bit read is random
*/
if (div == 0)
++div;
if (name) {
unsigned ahb_khz = (ar71xx_ahb_freq + 500) / 1000;
unsigned div_real = 2 * (div + 1);
printk(KERN_INFO "%s SPI clock %u kHz (AHB %u kHz / %u)\n",
name,
ahb_khz / div_real,
ahb_khz, div_real);
}
return SPI_CTRL_FASTEST + div;
}
static int __init rb4xx_nand_probe(struct platform_device *pdev)
{
struct rb4xx_nand_info *info;
int ret;
printk(KERN_INFO DRV_DESC " version " DRV_VERSION "\n");
ret = gpio_request(RB4XX_NAND_GPIO_RDY, "NAND RDY");
if (ret) {
printk(KERN_ERR "rb4xx-nand: gpio request failed\n");
return ret;
}
ret = gpio_direction_input(RB4XX_NAND_GPIO_RDY);
if (ret) {
printk(KERN_ERR "rb4xx-nand: unable to set input mode "
"on gpio%d\n", RB4XX_NAND_GPIO_RDY);
goto err_free_gpio;
}
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
printk(KERN_ERR "rb4xx-nand: no memory for private data\n");
ret = -ENOMEM;
goto err_free_gpio;
}
#if USE_FAST_READ
spi_ctrl_fread = get_spi_ctrl(RB4XX_NAND_HZ, "NAND");
#endif
spi_ctrl_flash = get_spi_ctrl(RB4XX_FLASH_HZ, "FLASH");
rb4xx_nand_write_cfg(CFG_BIT_nLEDS | CFG_BIT_nCE);
info->chip.priv = &info;
info->mtd.priv = &info->chip;
info->mtd.owner = THIS_MODULE;
info->chip.cmd_ctrl = rb4xx_nand_cmd_ctrl;
info->chip.dev_ready = rb4xx_nand_dev_ready;
info->chip.read_byte = rb4xx_nand_read_byte;
info->chip.write_buf = rb4xx_nand_write_buf;
info->chip.read_buf = rb4xx_nand_read_buf;
info->chip.verify_buf = rb4xx_nand_verify_buf;
info->chip.chip_delay = 25;
info->chip.ecc.mode = NAND_ECC_SOFT;
info->chip.options |= NAND_NO_AUTOINCR;
platform_set_drvdata(pdev, info);
ret = nand_scan_ident(&info->mtd, 1);
if (ret) {
ret = -ENXIO;
goto err_free_info;
}
if (info->mtd.writesize == 512)
info->chip.ecc.layout = &rb4xx_nand_ecclayout;
ret = nand_scan_tail(&info->mtd);
if (ret) {
return -ENXIO;
goto err_set_drvdata;
}
#ifdef CONFIG_MTD_PARTITIONS
ret = add_mtd_partitions(&info->mtd, rb4xx_nand_partitions,
ARRAY_SIZE(rb4xx_nand_partitions));
#else
ret = add_mtd_device(&info->mtd);
#endif
if (ret)
goto err_release_nand;
return 0;
err_release_nand:
nand_release(&info->mtd);
err_set_drvdata:
platform_set_drvdata(pdev, NULL);
err_free_info:
kfree(info);
err_free_gpio:
gpio_free(RB4XX_NAND_GPIO_RDY);
return ret;
}
static int __devexit rb4xx_nand_remove(struct platform_device *pdev)
{
struct rb4xx_nand_info *info = platform_get_drvdata(pdev);
nand_release(&info->mtd);
platform_set_drvdata(pdev, NULL);
kfree(info);
return 0;
}
static struct platform_driver rb4xx_nand_driver = {
.probe = rb4xx_nand_probe,
.remove = __devexit_p(rb4xx_nand_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};
static int __init rb4xx_nand_init(void)
{
return platform_driver_register(&rb4xx_nand_driver);
}
static void __exit rb4xx_nand_exit(void)
{
platform_driver_unregister(&rb4xx_nand_driver);
}
module_init(rb4xx_nand_init);
module_exit(rb4xx_nand_exit);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,7 @@
config AG71XX
tristate "Atheros AR71xx built-in ethernet mac support"
depends on ATHEROS_AR71XX
select PHYLIB
help
If you wish to compile a kernel for AR71xx/91xx and enable
ethernet support, then you should always answer Y to this.

View file

@ -0,0 +1,7 @@
#
# Makefile for the Atheros AR71xx built-in ethernet macs
#
obj-$(CONFIG_AG71XX) += ag71xx.o
ag71xx-objs := ag71xx_main.o ag71xx_mii.o ag71xx_ethtool.o

View file

@ -0,0 +1,281 @@
/*
* Atheros AR71xx built-in ethernet mac driver
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Based on Atheros' AG7100 driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef __AG71XX_H
#define __AG71XX_H
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/phy.h>
#include <linux/skbuff.h>
#include <linux/dma-mapping.h>
#include <linux/bitops.h>
#include <asm/mach-ar71xx/ar71xx.h>
#include <asm/mach-ar71xx/platform.h>
#define ETH_FCS_LEN 4
#define AG71XX_DRV_NAME "ag71xx"
#define AG71XX_DRV_VERSION "0.3.9"
#define AG71XX_NAPI_TX 1
#define AG71XX_NAPI_WEIGHT 64
#define AG71XX_INT_ERR (AG71XX_INT_RX_BE | AG71XX_INT_TX_BE)
#define AG71XX_INT_TX (AG71XX_INT_TX_PS)
#define AG71XX_INT_RX (AG71XX_INT_RX_PR | AG71XX_INT_RX_OF)
#ifdef AG71XX_NAPI_TX
#define AG71XX_INT_POLL (AG71XX_INT_RX | AG71XX_INT_TX)
#define AG71XX_INT_INIT (AG71XX_INT_ERR | AG71XX_INT_POLL)
#else
#define AG71XX_INT_POLL (AG71XX_INT_RX)
#define AG71XX_INT_INIT (AG71XX_INT_ERR | AG71XX_INT_POLL | AG71XX_INT_TX)
#endif
#define AG71XX_TX_FIFO_LEN 2048
#define AG71XX_TX_MTU_LEN 1536
#define AG71XX_RX_PKT_RESERVE 64
#define AG71XX_RX_PKT_SIZE \
(AG71XX_RX_PKT_RESERVE + ETH_HLEN + ETH_FRAME_LEN + ETH_FCS_LEN)
#define AG71XX_TX_RING_SIZE 64
#define AG71XX_TX_THRES_STOP (AG71XX_TX_RING_SIZE - 4)
#define AG71XX_TX_THRES_WAKEUP \
(AG71XX_TX_RING_SIZE - (AG71XX_TX_RING_SIZE / 4))
#define AG71XX_RX_RING_SIZE 128
#undef DEBUG
#ifdef DEBUG
#define DBG(fmt, args...) printk(KERN_DEBUG fmt, ## args)
#else
#define DBG(fmt, args...) do {} while (0)
#endif
#define ag71xx_assert(_cond) \
do { \
if (_cond) \
break; \
printk("%s,%d: assertion failed\n", __FILE__, __LINE__); \
BUG(); \
} while (0)
struct ag71xx_desc {
u32 data;
u32 ctrl;
#define DESC_EMPTY BIT(31)
#define DESC_MORE BIT(24)
#define DESC_PKTLEN_M 0x1fff
u32 next;
};
struct ag71xx_buf {
struct sk_buff *skb;
};
struct ag71xx_ring {
struct ag71xx_buf *buf;
struct ag71xx_desc *descs;
dma_addr_t descs_dma;
unsigned int curr;
unsigned int dirty;
unsigned int size;
};
struct ag71xx {
void __iomem *mac_base;
void __iomem *mii_ctrl;
spinlock_t lock;
struct platform_device *pdev;
struct net_device *dev;
struct napi_struct napi;
struct ag71xx_ring rx_ring;
struct ag71xx_ring tx_ring;
struct phy_device *phy_dev;
struct mii_bus mii_bus;
unsigned int link;
unsigned int speed;
int duplex;
};
extern struct ethtool_ops ag71xx_ethtool_ops;
extern int ag71xx_mdio_init(struct ag71xx *ag, int id);
extern void ag71xx_mdio_cleanup(struct ag71xx *ag);
extern int ag71xx_mii_peek(struct ag71xx *ag);
extern void ag71xx_mii_ctrl_set_if(struct ag71xx *ag, unsigned int mii_if);
extern void ag71xx_mii_ctrl_set_speed(struct ag71xx *ag, unsigned int speed);
extern void ag71xx_link_update(struct ag71xx *ag);
static inline struct ag71xx_platform_data *ag71xx_get_pdata(struct ag71xx *ag)
{
return ag->pdev->dev.platform_data;
}
static inline void ag71xx_wr(struct ag71xx *ag, unsigned reg, u32 value)
{
__raw_writel(value, ag->mac_base + reg);
}
static inline u32 ag71xx_rr(struct ag71xx *ag, unsigned reg)
{
return __raw_readl(ag->mac_base + reg);
}
static inline void ag71xx_sb(struct ag71xx *ag, unsigned reg, u32 mask)
{
void __iomem *r = ag->mac_base + reg;
__raw_writel(__raw_readl(r) | mask, r);
}
static inline void ag71xx_cb(struct ag71xx *ag, unsigned reg, u32 mask)
{
void __iomem *r = ag->mac_base + reg;
__raw_writel(__raw_readl(r) & ~mask, r);
}
static inline int ag71xx_desc_empty(struct ag71xx_desc *desc)
{
return ((desc->ctrl & DESC_EMPTY) != 0);
}
static inline int ag71xx_desc_pktlen(struct ag71xx_desc *desc)
{
return (desc->ctrl & DESC_PKTLEN_M);
}
/* Register offsets */
#define AG71XX_REG_MAC_CFG1 0x0000
#define AG71XX_REG_MAC_CFG2 0x0004
#define AG71XX_REG_MAC_IPG 0x0008
#define AG71XX_REG_MAC_HDX 0x000c
#define AG71XX_REG_MAC_MFL 0x0010
#define AG71XX_REG_MII_CFG 0x0020
#define AG71XX_REG_MII_CMD 0x0024
#define AG71XX_REG_MII_ADDR 0x0028
#define AG71XX_REG_MII_CTRL 0x002c
#define AG71XX_REG_MII_STATUS 0x0030
#define AG71XX_REG_MII_IND 0x0034
#define AG71XX_REG_MAC_IFCTL 0x0038
#define AG71XX_REG_MAC_ADDR1 0x0040
#define AG71XX_REG_MAC_ADDR2 0x0044
#define AG71XX_REG_FIFO_CFG0 0x0048
#define AG71XX_REG_FIFO_CFG1 0x004c
#define AG71XX_REG_FIFO_CFG2 0x0050
#define AG71XX_REG_FIFO_CFG3 0x0054
#define AG71XX_REG_FIFO_CFG4 0x0058
#define AG71XX_REG_FIFO_CFG5 0x005c
#define AG71XX_REG_FIFO_RAM0 0x0060
#define AG71XX_REG_FIFO_RAM1 0x0064
#define AG71XX_REG_FIFO_RAM2 0x0068
#define AG71XX_REG_FIFO_RAM3 0x006c
#define AG71XX_REG_FIFO_RAM4 0x0070
#define AG71XX_REG_FIFO_RAM5 0x0074
#define AG71XX_REG_FIFO_RAM6 0x0078
#define AG71XX_REG_FIFO_RAM7 0x007c
#define AG71XX_REG_TX_CTRL 0x0180
#define AG71XX_REG_TX_DESC 0x0184
#define AG71XX_REG_TX_STATUS 0x0188
#define AG71XX_REG_RX_CTRL 0x018c
#define AG71XX_REG_RX_DESC 0x0190
#define AG71XX_REG_RX_STATUS 0x0194
#define AG71XX_REG_INT_ENABLE 0x0198
#define AG71XX_REG_INT_STATUS 0x019c
#define MAC_CFG1_TXE BIT(0)
#define MAC_CFG1_STX BIT(1)
#define MAC_CFG1_RXE BIT(2)
#define MAC_CFG1_SRX BIT(3)
#define MAC_CFG1_LB BIT(8)
#define MAC_CFG1_SR BIT(31)
#define MAC_CFG2_FDX BIT(0)
#define MAC_CFG2_CRC_EN BIT(1)
#define MAC_CFG2_PAD_CRC_EN BIT(2)
#define MAC_CFG2_LEN_CHECK BIT(4)
#define MAC_CFG2_HUGE_FRAME_EN BIT(5)
#define MAC_CFG2_IF_1000 BIT(9)
#define MAC_CFG2_IF_10_100 BIT(8)
#define AG71XX_INT_TX_PS BIT(0)
#define AG71XX_INT_TX_UR BIT(1)
#define AG71XX_INT_TX_BE BIT(3)
#define AG71XX_INT_RX_PR BIT(4)
#define AG71XX_INT_RX_OF BIT(6)
#define AG71XX_INT_RX_BE BIT(7)
#define MAC_IFCTL_SPEED BIT(16)
#define MII_CFG_CLK_DIV_4 0
#define MII_CFG_CLK_DIV_6 2
#define MII_CFG_CLK_DIV_8 3
#define MII_CFG_CLK_DIV_10 4
#define MII_CFG_CLK_DIV_14 5
#define MII_CFG_CLK_DIV_20 6
#define MII_CFG_CLK_DIV_28 7
#define MII_CMD_WRITE 0x0
#define MII_CMD_READ 0x1
#define MII_ADDR_S 8
#define MII_IND_BUSY BIT(0)
#define MII_IND_INVALID BIT(2)
#define TX_CTRL_TXE BIT(0)
#define TX_STATUS_PS BIT(0)
#define TX_STATUS_UR BIT(1)
#define TX_STATUS_BE BIT(3)
#define RX_CTRL_RXE BIT(0)
#define RX_STATUS_PR BIT(0)
#define RX_STATUS_OF BIT(1)
#define RX_STATUS_BE BIT(3)
#define FIFO_CFG5_BYTE_PER_CLK BIT(19)
#define MII_CTRL_SPEED_S 4
#define MII_CTRL_SPEED_M 3
#define MII_CTRL_SPEED_10 0
#define MII_CTRL_SPEED_100 1
#define MII_CTRL_SPEED_1000 2
static inline void ag71xx_int_enable(struct ag71xx *ag, u32 ints)
{
ag71xx_sb(ag, AG71XX_REG_INT_ENABLE, ints);
}
static inline void ag71xx_int_disable(struct ag71xx *ag, u32 ints)
{
ag71xx_cb(ag, AG71XX_REG_INT_ENABLE, ints);
}
#endif /* _AG71XX_H */

View file

@ -0,0 +1,55 @@
/*
* Atheros AR71xx built-in ethernet mac driver
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Based on Atheros' AG7100 driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include "ag71xx.h"
static int ag71xx_ethtool_get_settings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
struct ag71xx *ag = netdev_priv(dev);
struct phy_device *phydev = ag->phy_dev;
if (!phydev)
return -ENODEV;
return phy_ethtool_gset(phydev, cmd);
}
static int ag71xx_ethtool_set_settings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
struct ag71xx *ag = netdev_priv(dev);
struct phy_device *phydev = ag->phy_dev;
if (!phydev)
return -ENODEV;
return phy_ethtool_sset(phydev, cmd);
}
static void ag71xx_ethtool_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct ag71xx *ag = netdev_priv(dev);
strcpy(info->driver, ag->pdev->dev.driver->name);
strcpy(info->version, AG71XX_DRV_VERSION);
strcpy(info->bus_info, ag->pdev->dev.bus_id);
}
struct ethtool_ops ag71xx_ethtool_ops = {
.set_settings = ag71xx_ethtool_set_settings,
.get_settings = ag71xx_ethtool_get_settings,
.get_drvinfo = ag71xx_ethtool_get_drvinfo,
.get_link = ethtool_op_get_link,
};

View file

@ -0,0 +1,855 @@
/*
* Atheros AR71xx built-in ethernet mac driver
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Based on Atheros' AG7100 driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include "ag71xx.h"
static void ag71xx_dump_regs(struct ag71xx *ag)
{
DBG("%s: mac_cfg1=%08x, mac_cfg2=%08x, ipg=%08x, hdx=%08x, mfl=%08x\n",
ag->dev->name,
ag71xx_rr(ag, AG71XX_REG_MAC_CFG1),
ag71xx_rr(ag, AG71XX_REG_MAC_CFG2),
ag71xx_rr(ag, AG71XX_REG_MAC_IPG),
ag71xx_rr(ag, AG71XX_REG_MAC_HDX),
ag71xx_rr(ag, AG71XX_REG_MAC_MFL));
DBG("%s: mii_cfg=%08x, mii_cmd=%08x, mii_addr=%08x\n",
ag->dev->name,
ag71xx_rr(ag, AG71XX_REG_MII_CFG),
ag71xx_rr(ag, AG71XX_REG_MII_CMD),
ag71xx_rr(ag, AG71XX_REG_MII_ADDR));
DBG("%s: mii_ctrl=%08x, mii_status=%08x, mii_ind=%08x\n",
ag->dev->name,
ag71xx_rr(ag, AG71XX_REG_MII_CTRL),
ag71xx_rr(ag, AG71XX_REG_MII_STATUS),
ag71xx_rr(ag, AG71XX_REG_MII_IND));
DBG("%s: mac_ifctl=%08x, mac_addr1=%08x, mac_addr2=%08x\n",
ag->dev->name,
ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL),
ag71xx_rr(ag, AG71XX_REG_MAC_ADDR1),
ag71xx_rr(ag, AG71XX_REG_MAC_ADDR2));
DBG("%s: fifo_cfg0=%08x, fifo_cfg1=%08x, fifo_cfg2=%08x\n",
ag->dev->name,
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0),
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1),
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2));
DBG("%s: fifo_cfg3=%08x, fifo_cfg3=%08x, fifo_cfg5=%08x\n",
ag->dev->name,
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3),
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4),
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5));
}
static void ag71xx_ring_free(struct ag71xx_ring *ring)
{
kfree(ring->buf);
if (ring->descs)
dma_free_coherent(NULL, ring->size * sizeof(*ring->descs),
ring->descs, ring->descs_dma);
}
static int ag71xx_ring_alloc(struct ag71xx_ring *ring, unsigned int size)
{
int err;
ring->descs = dma_alloc_coherent(NULL, size * sizeof(*ring->descs),
&ring->descs_dma,
GFP_ATOMIC);
if (!ring->descs) {
err = -ENOMEM;
goto err;
}
ring->size = size;
ring->buf = kzalloc(size * sizeof(*ring->buf), GFP_KERNEL);
if (!ring->buf) {
err = -ENOMEM;
goto err;
}
return 0;
err:
return err;
}
static void ag71xx_ring_tx_clean(struct ag71xx *ag)
{
struct ag71xx_ring *ring = &ag->tx_ring;
struct net_device *dev = ag->dev;
while (ring->curr != ring->dirty) {
u32 i = ring->dirty % AG71XX_TX_RING_SIZE;
if (!ag71xx_desc_empty(&ring->descs[i])) {
ring->descs[i].ctrl = 0;
dev->stats.tx_errors++;
}
if (ring->buf[i].skb)
dev_kfree_skb_any(ring->buf[i].skb);
ring->buf[i].skb = NULL;
ring->dirty++;
}
/* flush descriptors */
wmb();
}
static void ag71xx_ring_tx_init(struct ag71xx *ag)
{
struct ag71xx_ring *ring = &ag->tx_ring;
int i;
for (i = 0; i < AG71XX_TX_RING_SIZE; i++) {
ring->descs[i].next = (u32) (ring->descs_dma +
sizeof(*ring->descs) * ((i + 1) % AG71XX_TX_RING_SIZE));
ring->descs[i].ctrl = DESC_EMPTY;
ring->buf[i].skb = NULL;
}
/* flush descriptors */
wmb();
ring->curr = 0;
ring->dirty = 0;
}
static void ag71xx_ring_rx_clean(struct ag71xx *ag)
{
struct ag71xx_ring *ring = &ag->rx_ring;
int i;
if (!ring->buf)
return;
for (i = 0; i < AG71XX_RX_RING_SIZE; i++)
if (ring->buf[i].skb)
kfree_skb(ring->buf[i].skb);
}
static int ag71xx_ring_rx_init(struct ag71xx *ag)
{
struct ag71xx_ring *ring = &ag->rx_ring;
unsigned int i;
int ret;
ret = 0;
for (i = 0; i < AG71XX_RX_RING_SIZE; i++)
ring->descs[i].next = (u32) (ring->descs_dma +
sizeof(*ring->descs) * ((i + 1) % AG71XX_RX_RING_SIZE));
for (i = 0; i < AG71XX_RX_RING_SIZE; i++) {
struct sk_buff *skb;
skb = dev_alloc_skb(AG71XX_RX_PKT_SIZE);
if (!skb) {
ret = -ENOMEM;
break;
}
skb->dev = ag->dev;
skb_reserve(skb, AG71XX_RX_PKT_RESERVE);
ring->buf[i].skb = skb;
ring->descs[i].data = virt_to_phys(skb->data);
ring->descs[i].ctrl = DESC_EMPTY;
}
/* flush descriptors */
wmb();
ring->curr = 0;
ring->dirty = 0;
return ret;
}
static int ag71xx_ring_rx_refill(struct ag71xx *ag)
{
struct ag71xx_ring *ring = &ag->rx_ring;
unsigned int count;
count = 0;
for (; ring->curr - ring->dirty > 0; ring->dirty++) {
unsigned int i;
i = ring->dirty % AG71XX_RX_RING_SIZE;
if (ring->buf[i].skb == NULL) {
struct sk_buff *skb;
skb = dev_alloc_skb(AG71XX_RX_PKT_SIZE);
if (skb == NULL) {
printk(KERN_ERR "%s: no memory for skb\n",
ag->dev->name);
break;
}
skb_reserve(skb, AG71XX_RX_PKT_RESERVE);
skb->dev = ag->dev;
ring->buf[i].skb = skb;
ring->descs[i].data = virt_to_phys(skb->data);
}
ring->descs[i].ctrl = DESC_EMPTY;
count++;
}
/* flush descriptors */
wmb();
DBG("%s: %u rx descriptors refilled\n", ag->dev->name, count);
return count;
}
static int ag71xx_rings_init(struct ag71xx *ag)
{
int ret;
ret = ag71xx_ring_alloc(&ag->tx_ring, AG71XX_TX_RING_SIZE);
if (ret)
return ret;
ag71xx_ring_tx_init(ag);
ret = ag71xx_ring_alloc(&ag->rx_ring, AG71XX_RX_RING_SIZE);
if (ret)
return ret;
ret = ag71xx_ring_rx_init(ag);
return ret;
}
static void ag71xx_rings_cleanup(struct ag71xx *ag)
{
ag71xx_ring_rx_clean(ag);
ag71xx_ring_free(&ag->rx_ring);
ag71xx_ring_tx_clean(ag);
ag71xx_ring_free(&ag->tx_ring);
}
static void ag71xx_hw_set_macaddr(struct ag71xx *ag, unsigned char *mac)
{
u32 t;
t = (((u32) mac[0]) << 24) | (((u32) mac[1]) << 16)
| (((u32) mac[2]) << 8) | ((u32) mac[2]);
ag71xx_wr(ag, AG71XX_REG_MAC_ADDR1, t);
t = (((u32) mac[4]) << 24) | (((u32) mac[5]) << 16);
ag71xx_wr(ag, AG71XX_REG_MAC_ADDR2, t);
}
#define MAC_CFG1_INIT (MAC_CFG1_RXE | MAC_CFG1_TXE | MAC_CFG1_SRX \
| MAC_CFG1_STX)
static void ag71xx_hw_init(struct ag71xx *ag)
{
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
ag71xx_sb(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_SR);
udelay(20);
ar71xx_device_stop(pdata->reset_bit);
mdelay(100);
ar71xx_device_start(pdata->reset_bit);
mdelay(100);
ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_INIT);
/* TODO: set max packet size */
ag71xx_sb(ag, AG71XX_REG_MAC_CFG2,
MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK);
ag71xx_wr(ag, AG71XX_REG_FIFO_CFG0, 0x00001f00);
ag71xx_mii_ctrl_set_if(ag, pdata->mii_if);
ag71xx_wr(ag, AG71XX_REG_MII_CFG, MII_CFG_CLK_DIV_28);
ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, 0x0fff0000);
ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, 0x00001fff);
ag71xx_wr(ag, AG71XX_REG_FIFO_CFG4, 0x0000ffff);
ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, 0x0007ffef);
}
static void ag71xx_hw_start(struct ag71xx *ag)
{
/* start RX engine */
ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE);
/* enable interrupts */
ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, AG71XX_INT_INIT);
}
static void ag71xx_hw_stop(struct ag71xx *ag)
{
/* stop RX and TX */
ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0);
ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0);
/* disable all interrupts */
ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, 0);
}
static int ag71xx_open(struct net_device *dev)
{
struct ag71xx *ag = netdev_priv(dev);
int ret;
ret = ag71xx_rings_init(ag);
if (ret)
goto err;
napi_enable(&ag->napi);
netif_carrier_off(dev);
if (ag->phy_dev) {
phy_start(ag->phy_dev);
} else {
ag->duplex = DUPLEX_FULL;
ag->speed = SPEED_100;
ag->link = 1;
ag71xx_link_update(ag);
}
ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma);
ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->rx_ring.descs_dma);
ag71xx_hw_set_macaddr(ag, dev->dev_addr);
ag71xx_hw_start(ag);
netif_start_queue(dev);
return 0;
err:
ag71xx_rings_cleanup(ag);
return ret;
}
static int ag71xx_stop(struct net_device *dev)
{
struct ag71xx *ag = netdev_priv(dev);
unsigned long flags;
spin_lock_irqsave(&ag->lock, flags);
netif_stop_queue(dev);
ag71xx_hw_stop(ag);
netif_carrier_off(dev);
if (ag->phy_dev) {
phy_stop(ag->phy_dev);
} else {
ag->duplex = -1;
ag->link = 0;
ag->speed = 0;
ag71xx_link_update(ag);
}
napi_disable(&ag->napi);
spin_unlock_irqrestore(&ag->lock, flags);
ag71xx_rings_cleanup(ag);
return 0;
}
static int ag71xx_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ag71xx *ag = netdev_priv(dev);
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
struct ag71xx_ring *ring = &ag->tx_ring;
struct ag71xx_desc *desc;
unsigned long flags;
int i;
i = ring->curr % AG71XX_TX_RING_SIZE;
desc = &ring->descs[i];
spin_lock_irqsave(&ag->lock, flags);
ar71xx_ddr_flush(pdata->flush_reg);
spin_unlock_irqrestore(&ag->lock, flags);
if (!ag71xx_desc_empty(desc))
goto err_drop;
if (skb->len <= 0) {
DBG("%s: packet len is too small\n", ag->dev->name);
goto err_drop;
}
dma_cache_wback_inv((unsigned long)skb->data, skb->len);
ring->buf[i].skb = skb;
/* setup descriptor fields */
desc->data = virt_to_phys(skb->data);
desc->ctrl = (skb->len & DESC_PKTLEN_M);
/* flush descriptor */
wmb();
ring->curr++;
if (ring->curr == (ring->dirty + AG71XX_TX_THRES_STOP)) {
DBG("%s: tx queue full\n", ag->dev->name);
netif_stop_queue(dev);
}
DBG("%s: packet injected into TX queue\n", ag->dev->name);
/* enable TX engine */
ag71xx_wr(ag, AG71XX_REG_TX_CTRL, TX_CTRL_TXE);
dev->trans_start = jiffies;
return 0;
err_drop:
dev->stats.tx_dropped++;
dev_kfree_skb(skb);
return 0;
}
static int ag71xx_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct mii_ioctl_data *data = (struct mii_ioctl_data *) &ifr->ifr_data;
struct ag71xx *ag = netdev_priv(dev);
int ret;
switch (cmd) {
case SIOCETHTOOL:
if (ag->phy_dev == NULL)
break;
spin_lock_irq(&ag->lock);
ret = phy_ethtool_ioctl(ag->phy_dev, (void *) ifr->ifr_data);
spin_unlock_irq(&ag->lock);
return ret;
case SIOCSIFHWADDR:
if (copy_from_user
(dev->dev_addr, ifr->ifr_data, sizeof(dev->dev_addr)))
return -EFAULT;
return 0;
case SIOCGIFHWADDR:
if (copy_to_user
(ifr->ifr_data, dev->dev_addr, sizeof(dev->dev_addr)))
return -EFAULT;
return 0;
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSMIIREG:
if (ag->phy_dev == NULL)
break;
return phy_mii_ioctl(ag->phy_dev, data, cmd);
default:
break;
}
return -EOPNOTSUPP;
}
static void ag71xx_tx_packets(struct ag71xx *ag)
{
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
struct ag71xx_ring *ring = &ag->tx_ring;
unsigned int sent;
DBG("%s: processing TX ring\n", ag->dev->name);
#ifdef AG71XX_NAPI_TX
ar71xx_ddr_flush(pdata->flush_reg);
#endif
sent = 0;
while (ring->dirty != ring->curr) {
unsigned int i = ring->dirty % AG71XX_TX_RING_SIZE;
struct ag71xx_desc *desc = &ring->descs[i];
struct sk_buff *skb = ring->buf[i].skb;
if (!ag71xx_desc_empty(desc))
break;
ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS);
ag->dev->stats.tx_bytes += skb->len;
ag->dev->stats.tx_packets++;
dev_kfree_skb_any(skb);
ring->buf[i].skb = NULL;
ring->dirty++;
sent++;
}
DBG("%s: %d packets sent out\n", ag->dev->name, sent);
if ((ring->curr - ring->dirty) < AG71XX_TX_THRES_WAKEUP)
netif_wake_queue(ag->dev);
}
static int ag71xx_rx_packets(struct ag71xx *ag, int limit)
{
struct net_device *dev = ag->dev;
struct ag71xx_ring *ring = &ag->rx_ring;
#ifndef AG71XX_NAPI_TX
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
unsigned long flags;
#endif
int done = 0;
#ifndef AG71XX_NAPI_TX
spin_lock_irqsave(&ag->lock, flags);
ar71xx_ddr_flush(pdata->flush_reg);
spin_unlock_irqrestore(&ag->lock, flags);
#endif
DBG("%s: rx packets, limit=%d, curr=%u, dirty=%u\n",
dev->name, limit, ring->curr, ring->dirty);
while (done < limit) {
unsigned int i = ring->curr % AG71XX_RX_RING_SIZE;
struct ag71xx_desc *desc = &ring->descs[i];
struct sk_buff *skb;
int pktlen;
if (ag71xx_desc_empty(desc))
break;
if ((ring->dirty + AG71XX_RX_RING_SIZE) == ring->curr) {
ag71xx_assert(0);
break;
}
skb = ring->buf[i].skb;
pktlen = ag71xx_desc_pktlen(desc);
pktlen -= ETH_FCS_LEN;
/* TODO: move it into the refill function */
dma_cache_wback_inv((unsigned long)skb->data, pktlen);
skb_put(skb, pktlen);
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
netif_receive_skb(skb);
dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pktlen;
ring->buf[i].skb = NULL;
done++;
ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR);
ring->curr++;
if ((ring->curr - ring->dirty) > (AG71XX_RX_RING_SIZE / 4))
ag71xx_ring_rx_refill(ag);
}
ag71xx_ring_rx_refill(ag);
DBG("%s: rx finish, curr=%u, dirty=%u, done=%d\n",
dev->name, ring->curr, ring->dirty, done);
return done;
}
static int ag71xx_poll(struct napi_struct *napi, int limit)
{
struct ag71xx *ag = container_of(napi, struct ag71xx, napi);
#ifdef AG71XX_NAPI_TX
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
#endif
struct net_device *dev = ag->dev;
unsigned long flags;
u32 status;
int done;
#ifdef AG71XX_NAPI_TX
ar71xx_ddr_flush(pdata->flush_reg);
ag71xx_tx_packets(ag);
#endif
DBG("%s: processing RX ring\n", dev->name);
done = ag71xx_rx_packets(ag, limit);
/* TODO: add OOM handler */
status = ag71xx_rr(ag, AG71XX_REG_INT_STATUS);
status &= AG71XX_INT_POLL;
if ((done < limit) && (!status)) {
DBG("%s: disable polling mode, done=%d, status=%x\n",
dev->name, done, status);
netif_rx_complete(dev, napi);
/* enable interrupts */
spin_lock_irqsave(&ag->lock, flags);
ag71xx_int_enable(ag, AG71XX_INT_POLL);
spin_unlock_irqrestore(&ag->lock, flags);
return 0;
}
if (status & AG71XX_INT_RX_OF) {
printk(KERN_ALERT "%s: rx owerflow, restarting dma\n",
dev->name);
/* ack interrupt */
ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_OF);
/* restart RX */
ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE);
}
DBG("%s: stay in polling mode, done=%d, status=%x\n",
dev->name, done, status);
return 1;
}
static irqreturn_t ag71xx_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct ag71xx *ag = netdev_priv(dev);
u32 status;
status = ag71xx_rr(ag, AG71XX_REG_INT_STATUS);
status &= ag71xx_rr(ag, AG71XX_REG_INT_ENABLE);
if (unlikely(!status))
return IRQ_NONE;
if (unlikely(status & AG71XX_INT_ERR)) {
if (status & AG71XX_INT_TX_BE) {
ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE);
dev_err(&dev->dev, "TX BUS error\n");
}
if (status & AG71XX_INT_RX_BE) {
ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE);
dev_err(&dev->dev, "RX BUS error\n");
}
}
#if 0
if (unlikely(status & AG71XX_INT_TX_UR)) {
ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_UR);
DBG("%s: TX underrun\n", dev->name);
}
#endif
#ifndef AG71XX_NAPI_TX
if (likely(status & AG71XX_INT_TX_PS))
ag71xx_tx_packets(ag);
#endif
if (likely(status & AG71XX_INT_POLL)) {
ag71xx_int_disable(ag, AG71XX_INT_POLL);
DBG("%s: enable polling mode\n", dev->name);
netif_rx_schedule(dev, &ag->napi);
}
return IRQ_HANDLED;
}
static void ag71xx_set_multicast_list(struct net_device *dev)
{
/* TODO */
}
static int __init ag71xx_probe(struct platform_device *pdev)
{
static u8 default_mac[ETH_ALEN] = {0x00, 0xba, 0xdb, 0xad, 0xba, 0xd0};
struct net_device *dev;
struct resource *res;
struct ag71xx *ag;
int err;
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "no platform data specified\n");
err = -ENXIO;
goto err_out;
}
dev = alloc_etherdev(sizeof(*ag));
if (!dev) {
dev_err(&pdev->dev, "alloc_etherdev failed\n");
err = -ENOMEM;
goto err_out;
}
SET_NETDEV_DEV(dev, &pdev->dev);
ag = netdev_priv(dev);
ag->pdev = pdev;
ag->dev = dev;
spin_lock_init(&ag->lock);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac_base");
if (!res) {
dev_err(&pdev->dev, "no mac_base resource found\n");
err = -ENXIO;
goto err_out;
}
ag->mac_base = ioremap_nocache(res->start, res->end - res->start + 1);
if (!ag->mac_base) {
dev_err(&pdev->dev, "unable to ioremap mac_base\n");
err = -ENOMEM;
goto err_free_dev;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mii_ctrl");
if (!res) {
dev_err(&pdev->dev, "no mii_ctrl resource found\n");
err = -ENXIO;
goto err_unmap_base;
}
ag->mii_ctrl = ioremap_nocache(res->start, res->end - res->start + 1);
if (!ag->mii_ctrl) {
dev_err(&pdev->dev, "unable to ioremap mii_ctrl\n");
err = -ENOMEM;
goto err_unmap_base;
}
dev->irq = platform_get_irq(pdev, 0);
err = request_irq(dev->irq, ag71xx_interrupt,
IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
dev->name, dev);
if (err) {
dev_err(&pdev->dev, "unable to request IRQ %d\n", dev->irq);
goto err_unmap_mii_ctrl;
}
dev->base_addr = (unsigned long)ag->mac_base;
dev->open = ag71xx_open;
dev->stop = ag71xx_stop;
dev->hard_start_xmit = ag71xx_hard_start_xmit;
dev->set_multicast_list = ag71xx_set_multicast_list;
dev->do_ioctl = ag71xx_do_ioctl;
dev->ethtool_ops = &ag71xx_ethtool_ops;
netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT);
memcpy(dev->dev_addr, default_mac, ETH_ALEN);
dev->dev_addr[5] += pdev->id & 0xff;
err = register_netdev(dev);
if (err) {
dev_err(&pdev->dev, "unable to register net device\n");
goto err_free_irq;
}
printk(KERN_INFO "%s: Atheros AG71xx at 0x%08lx, irq %d\n",
dev->name, dev->base_addr, dev->irq);
ag71xx_dump_regs(ag);
ag71xx_hw_init(ag);
ag71xx_dump_regs(ag);
err = ag71xx_mdio_init(ag, pdev->id);
if (err)
goto err_unregister_netdev;
platform_set_drvdata(pdev, dev);
return 0;
err_unregister_netdev:
unregister_netdev(dev);
err_free_irq:
free_irq(dev->irq, dev);
err_unmap_mii_ctrl:
iounmap(ag->mii_ctrl);
err_unmap_base:
iounmap(ag->mac_base);
err_free_dev:
kfree(dev);
err_out:
platform_set_drvdata(pdev, NULL);
return err;
}
static int __exit ag71xx_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
if (dev) {
struct ag71xx *ag = netdev_priv(dev);
if (ag->phy_dev)
phy_disconnect(ag->phy_dev);
ag71xx_mdio_cleanup(ag);
unregister_netdev(dev);
free_irq(dev->irq, dev);
iounmap(ag->mii_ctrl);
iounmap(ag->mac_base);
kfree(dev);
platform_set_drvdata(pdev, NULL);
}
return 0;
}
static struct platform_driver ag71xx_driver = {
.probe = ag71xx_probe,
.remove = __exit_p(ag71xx_remove),
.driver = {
.name = AG71XX_DRV_NAME,
}
};
static int __init ag71xx_module_init(void)
{
return platform_driver_register(&ag71xx_driver);
}
static void __exit ag71xx_module_exit(void)
{
platform_driver_unregister(&ag71xx_driver);
}
module_init(ag71xx_module_init);
module_exit(ag71xx_module_exit);
MODULE_VERSION(AG71XX_DRV_VERSION);
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" AG71XX_DRV_NAME);

View file

@ -0,0 +1,436 @@
/*
* Atheros AR71xx built-in ethernet mac driver
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Based on Atheros' AG7100 driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include "ag71xx.h"
#define AG71XX_MII_RETRY 1000
#define AG71XX_MII_DELAY 5
static inline void ag71xx_mii_ctrl_wr(struct ag71xx *ag, u32 value)
{
__raw_writel(value, ag->mii_ctrl);
}
static inline u32 ag71xx_mii_ctrl_rr(struct ag71xx *ag)
{
return __raw_readl(ag->mii_ctrl);
}
void ag71xx_mii_ctrl_set_if(struct ag71xx *ag, unsigned int mii_if)
{
u32 t;
t = ag71xx_mii_ctrl_rr(ag);
t &= ~(0x3);
t |= (mii_if & 0x3);
ag71xx_mii_ctrl_wr(ag, t);
}
void ag71xx_mii_ctrl_set_speed(struct ag71xx *ag, unsigned int speed)
{
u32 t;
t = ag71xx_mii_ctrl_rr(ag);
t &= ~(0x3 << 4);
t |= (speed & 0x3) << 4;
ag71xx_mii_ctrl_wr(ag, t);
}
static int ag71xx_mii_read(struct ag71xx *ag, int addr, int reg)
{
int ret;
int i;
ag71xx_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
ag71xx_wr(ag, AG71XX_REG_MII_ADDR,
((addr & 0xff) << MII_ADDR_S) | (reg & 0xff));
ag71xx_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_READ);
i = AG71XX_MII_RETRY;
while (ag71xx_rr(ag, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
if (i-- == 0) {
printk(KERN_ERR "%s: mii_read timed out\n",
ag->dev->name);
ret = 0xffff;
goto out;
}
udelay(AG71XX_MII_DELAY);
}
ret = ag71xx_rr(ag, AG71XX_REG_MII_STATUS) & 0xffff;
ag71xx_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret);
out:
return ret;
}
static void ag71xx_mii_write(struct ag71xx *ag, int addr, int reg, u16 val)
{
int i;
DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val);
ag71xx_wr(ag, AG71XX_REG_MII_ADDR,
((addr & 0xff) << MII_ADDR_S) | (reg & 0xff));
ag71xx_wr(ag, AG71XX_REG_MII_CTRL, val);
i = AG71XX_MII_RETRY;
while (ag71xx_rr(ag, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
if (i-- == 0) {
printk(KERN_ERR "%s: mii_write timed out\n",
ag->dev->name);
break;
}
udelay(AG71XX_MII_DELAY);
}
}
int ag71xx_mii_peek(struct ag71xx *ag)
{
int cnt;
int i;
cnt = 0;
for (i = 0; i < PHY_MAX_ADDR; i++) {
u16 bmsr, id1, id2, bmcr, advert, lpa;
bmsr = ag71xx_mii_read(ag, i, MII_BMSR);
bmcr = ag71xx_mii_read(ag, i, MII_BMCR);
id1 = ag71xx_mii_read(ag, i, MII_PHYSID1);
id2 = ag71xx_mii_read(ag, i, MII_PHYSID2);
advert = ag71xx_mii_read(ag, i, MII_ADVERTISE);
lpa = ag71xx_mii_read(ag, i, MII_LPA);
DBG("%s: phy%02d bmsr=%04x, bmcr=%04x, "
"id=%04x.%04x, advertise=%04x, lpa=%04x\n",
ag->dev->name, i,
bmsr, bmcr, id1, id2, advert, lpa);
if ((bmsr | bmcr | id1 | id2 | advert | lpa) != 0)
cnt++;
}
return cnt;
}
#define PLL_SEC_CONFIG 0x18050004
#define PLL_ETH0_INT_CLOCK 0x18050010
#define PLL_ETH1_INT_CLOCK 0x18050014
#define PLL_ETH_EXT_CLOCK 0x18050018
#define ag7100_pll_shift(_ag) (((_ag)->pdev->id) ? 19 : 17)
#define ag7100_pll_offset(_ag) (((_ag)->pdev->id) ? PLL_ETH1_INT_CLOCK \
: PLL_ETH0_INT_CLOCK)
static void ag71xx_set_pll(struct ag71xx *ag, u32 pll_val)
{
void __iomem *pll_reg = ioremap_nocache(ag7100_pll_offset(ag), 4);
void __iomem *pll_cfg = ioremap_nocache(PLL_SEC_CONFIG, 4);
u32 s;
u32 t;
s = ag7100_pll_shift(ag);
t = __raw_readl(pll_cfg);
t &= ~(3 << s);
t |= (2 << s);
__raw_writel(t, pll_cfg);
udelay(100);
__raw_writel(pll_val, pll_reg);
t |= (3 << s);
__raw_writel(t, pll_cfg);
udelay(100);
t &= ~(3 << s);
__raw_writel(t, pll_cfg);
udelay(100);
DBG("%s: pll_reg %#x: %#x\n", ag->dev->name,
(unsigned int)pll_reg, __raw_readl(pll_reg));
iounmap(pll_cfg);
iounmap(pll_reg);
}
static unsigned char *ag71xx_speed_str(struct ag71xx *ag)
{
switch (ag->speed) {
case SPEED_1000:
return "1000";
case SPEED_100:
return "100";
case SPEED_10:
return "10";
}
return "?";
}
#if 1
#define PLL_VAL_1000 0x00110000
#define PLL_VAL_100 0x00001099
#define PLL_VAL_10 0x00991099
#else
#define PLL_VAL_1000 0x01111000
#define PLL_VAL_100 0x09991000
#define PLL_VAL_10 0x09991999
#endif
void ag71xx_link_update(struct ag71xx *ag)
{
u32 cfg2;
u32 ifctl;
u32 pll;
u32 fifo5;
u32 mii_speed;
if (!ag->link) {
netif_carrier_off(ag->dev);
printk(KERN_INFO "%s: link down\n", ag->dev->name);
return;
}
cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2);
cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX);
cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0;
ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL);
ifctl &= ~(MAC_IFCTL_SPEED);
fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5);
fifo5 &= ~FIFO_CFG5_BYTE_PER_CLK;
switch (ag->speed) {
case SPEED_1000:
mii_speed = MII_CTRL_SPEED_1000;
cfg2 |= MAC_CFG2_IF_1000;
pll = PLL_VAL_1000;
fifo5 |= FIFO_CFG5_BYTE_PER_CLK;
break;
case SPEED_100:
mii_speed = MII_CTRL_SPEED_100;
cfg2 |= MAC_CFG2_IF_10_100;
ifctl |= MAC_IFCTL_SPEED;
pll = PLL_VAL_100;
break;
case SPEED_10:
mii_speed = MII_CTRL_SPEED_10;
cfg2 |= MAC_CFG2_IF_10_100;
pll = PLL_VAL_10;
break;
default:
BUG();
return;
}
ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x008001ff);
ag71xx_set_pll(ag, pll);
ag71xx_mii_ctrl_set_speed(ag, mii_speed);
ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2);
ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5);
ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl);
netif_carrier_on(ag->dev);
printk(KERN_INFO "%s: link up (%sMbps/%s duplex)\n",
ag->dev->name,
ag71xx_speed_str(ag),
(DUPLEX_FULL == ag->duplex) ? "Full" : "Half");
DBG("%s: fifo1=%#x, fifo2=%#x, fifo3=%#x, fifo4=%#x, fifo5=%#x\n",
ag->dev->name,
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1),
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2),
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3),
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4),
ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5));
DBG("%s: mac_cfg2=%#x, ifctl=%#x, mii_ctrl=%#x\n",
ag->dev->name,
ag71xx_rr(ag, AG71XX_REG_MAC_CFG2),
ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL),
ag71xx_mii_ctrl_rr(ag));
}
static void ag71xx_link_adjust(struct net_device *dev)
{
struct ag71xx *ag = netdev_priv(dev);
struct phy_device *phydev = ag->phy_dev;
unsigned long flags;
int status_change = 0;
spin_lock_irqsave(&ag->lock, flags);
if (phydev->link) {
if (ag->duplex != phydev->duplex
|| ag->speed != phydev->speed) {
status_change = 1;
}
}
if (phydev->link != ag->link) {
if (phydev->link)
netif_schedule(dev);
status_change = 1;
}
ag->link = phydev->link;
ag->duplex = phydev->duplex;
ag->speed = phydev->speed;
if (status_change)
ag71xx_link_update(ag);
spin_unlock_irqrestore(&ag->lock, flags);
}
static int ag71xx_mdio_read(struct mii_bus *bus, int addr, int reg)
{
struct ag71xx *ag = bus->priv;
return ag71xx_mii_read(ag, addr, reg);
}
static int ag71xx_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val)
{
struct ag71xx *ag = bus->priv;
ag71xx_mii_write(ag, addr, reg, val);
return 0;
}
static int ag71xx_mdio_reset(struct mii_bus *bus)
{
/* TODO */
return 0;
}
static int ag71xx_mdio_probe(struct ag71xx *ag)
{
struct net_device *dev = ag->dev;
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
struct phy_device *phydev = NULL;
int phy_count = 0;
int phy_addr;
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
if (!(pdata->phy_mask & (1 << phy_addr)))
continue;
if (ag->mii_bus.phy_map[phy_addr] == NULL)
continue;
DBG("%s: PHY found at %s, uid=%08x\n",
dev->name,
ag->mii_bus.phy_map[phy_addr]->dev.bus_id,
ag->mii_bus.phy_map[phy_addr]->phy_id);
if (phydev == NULL)
phydev = ag->mii_bus.phy_map[phy_addr];
phy_count++;
}
switch (phy_count) {
case 0:
printk(KERN_ERR "%s: no PHY found\n", dev->name);
return -ENODEV;
case 1:
ag->phy_dev = phy_connect(dev, phydev->dev.bus_id,
&ag71xx_link_adjust, 0, pdata->phy_if_mode);
if (IS_ERR(ag->phy_dev)) {
printk(KERN_ERR "%s: could not connect to PHY at %s\n",
dev->name, phydev->dev.bus_id);
return PTR_ERR(ag->phy_dev);
}
/* mask with MAC supported features */
phydev->supported &= (SUPPORTED_10baseT_Half
| SUPPORTED_10baseT_Full
| SUPPORTED_100baseT_Half
| SUPPORTED_100baseT_Full
| SUPPORTED_Autoneg
| SUPPORTED_MII
| SUPPORTED_TP);
phydev->advertising = phydev->supported;
printk(KERN_DEBUG "%s: connected to PHY at %s "
"[uid=%08x, driver=%s]\n",
dev->name, phydev->dev.bus_id,
phydev->phy_id, phydev->drv->name);
ag->link = 0;
ag->speed = 0;
ag->duplex = -1;
break;
default:
ag->phy_dev = NULL;
printk(KERN_DEBUG "%s: connected to multiple PHYs (%d)\n",
dev->name, phy_count);
break;
}
return 0;
}
int ag71xx_mdio_init(struct ag71xx *ag, int id)
{
int err;
int i;
ag->mii_bus.name = "ag71xx_mii";
ag->mii_bus.read = ag71xx_mdio_read;
ag->mii_bus.write = ag71xx_mdio_write;
ag->mii_bus.reset = ag71xx_mdio_reset;
ag->mii_bus.id = id;
ag->mii_bus.priv = ag;
ag->mii_bus.dev = &ag->dev->dev;
ag->mii_bus.irq = kmalloc(sizeof(*ag->mii_bus.irq) * PHY_MAX_ADDR,
GFP_KERNEL);
if (!ag->mii_bus.irq) {
err = -ENOMEM;
goto err_out;
}
for (i = 0; i < PHY_MAX_ADDR; i++)
ag->mii_bus.irq[i] = PHY_POLL;
err = mdiobus_register(&ag->mii_bus);
if (err)
goto err_free_irqs;
err = ag71xx_mdio_probe(ag);
if (err)
goto err_unregister_bus;
return 0;
err_unregister_bus:
mdiobus_unregister(&ag->mii_bus);
err_free_irqs:
kfree(ag->mii_bus.irq);
err_out:
return err;
}
void ag71xx_mdio_cleanup(struct ag71xx *ag)
{
mdiobus_unregister(&ag->mii_bus);
kfree(ag->mii_bus.irq);
}

View file

@ -0,0 +1,82 @@
/*
* Driver for Micrel/Kendin PHYs
*
* Copyright (c) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
*/
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/phy.h>
#define KSZ_REG_INT_CTRL 0x1b
#define KSZ_INT_LU_EN (1 << 8) /* enable Link Up interrupt */
#define KSZ_INT_RF_EN (1 << 9) /* enable Remote Fault interrupt */
#define KSZ_INT_LD_EN (1 << 10) /* enable Link Down interrupt */
#define KSZ_INT_INIT (KSZ_INT_LU_EN | KSZ_INT_LD_EN)
static int ksz8041_ack_interrupt(struct phy_device *phydev)
{
int err;
err = phy_read(phydev, KSZ_REG_INT_CTRL);
return (err < 0) ? err : 0;
}
static int ksz8041_config_intr(struct phy_device *phydev)
{
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, KSZ_REG_INT_CTRL,
KSZ_INT_INIT);
else
err = phy_write(phydev, KSZ_REG_INT_CTRL, 0);
return err;
}
static struct phy_driver ksz8041_phy_driver = {
.phy_id = 0x00221512,
.name = "Micrel/Kendin KSZ8041",
.phy_id_mask = 0x001fffff,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = ksz8041_ack_interrupt,
.config_intr = ksz8041_config_intr,
.driver = {
.owner = THIS_MODULE,
},
};
static int __init micrel_phy_init(void)
{
int ret;
ret = phy_driver_register(&ksz8041_phy_driver);
return ret;
}
static void __exit micrel_phy_exit(void)
{
phy_driver_unregister(&ksz8041_phy_driver);
}
module_init(micrel_phy_init);
module_exit(micrel_phy_exit);
MODULE_DESCRIPTION("Micrel/Kendin PHY driver");
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,240 @@
/*
* Atheros AR71xx SPI Controller driver
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/bitops.h>
#include <asm/mach-ar71xx/ar71xx.h>
#include <asm/mach-ar71xx/platform.h>
#define DRV_DESC "Atheros AR71xx SPI Controller driver"
#define DRV_VERSION "0.2.2"
#define DRV_NAME "ar71xx-spi"
#undef PER_BIT_READ
struct ar71xx_spi {
struct spi_bitbang bitbang;
u32 ioc_base;
u32 reg_ctrl;
void __iomem *base;
struct platform_device *pdev;
u32 (*get_ioc_base)(u8 chip_select, int cs_high,
int is_on);
};
static inline u32 ar71xx_spi_rr(struct ar71xx_spi *sp, unsigned reg)
{
return __raw_readl(sp->base + reg);
}
static inline void ar71xx_spi_wr(struct ar71xx_spi *sp, unsigned reg, u32 val)
{
__raw_writel(val, sp->base + reg);
}
static inline struct ar71xx_spi *spidev_to_sp(struct spi_device *spi)
{
return spi_master_get_devdata(spi->master);
}
static u32 ar71xx_spi_get_ioc_base(u8 chip_select, int cs_high, int is_on)
{
u32 ret;
if (is_on == AR71XX_SPI_CS_INACTIVE)
ret = SPI_IOC_CS_ALL;
else
ret = SPI_IOC_CS_ALL & ~SPI_IOC_CS(chip_select);
return ret;
}
static void ar71xx_spi_chipselect(struct spi_device *spi, int value)
{
struct ar71xx_spi *sp = spidev_to_sp(spi);
void __iomem *base = sp->base;
u32 ioc_base;
switch (value) {
case BITBANG_CS_INACTIVE:
ioc_base = sp->get_ioc_base(spi->chip_select,
(spi->mode & SPI_CS_HIGH) != 0,
AR71XX_SPI_CS_INACTIVE);
__raw_writel(ioc_base, base + SPI_REG_IOC);
__raw_writel(sp->reg_ctrl, base + SPI_REG_CTRL);
__raw_writel(0, base + SPI_REG_FS);
break;
case BITBANG_CS_ACTIVE:
ioc_base = sp->get_ioc_base(spi->chip_select,
(spi->mode & SPI_CS_HIGH) != 0,
AR71XX_SPI_CS_ACTIVE);
__raw_writel(SPI_FS_GPIO, base + SPI_REG_FS);
/* TODO: setup speed */
__raw_writel(0x43, base + SPI_REG_CTRL);
__raw_writel(ioc_base, base + SPI_REG_IOC);
sp->ioc_base = ioc_base;
break;
}
}
static u32 ar71xx_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs,
u32 word, u8 bits)
{
struct ar71xx_spi *sp = spidev_to_sp(spi);
void __iomem *base = sp->base;
u32 ioc = sp->ioc_base;
u32 ret;
/* clock starts at inactive polarity */
for (word <<= (32 - bits); likely(bits); bits--) {
u32 out;
if (word & (1 << 31))
out = ioc | SPI_IOC_DO;
else
out = ioc & ~SPI_IOC_DO;
/* setup MSB (to slave) on trailing edge */
__raw_writel(out, base + SPI_REG_IOC);
__raw_writel(out | SPI_IOC_CLK, base + SPI_REG_IOC);
word <<= 1;
#ifdef PER_BIT_READ
/* sample MSB (from slave) on leading edge */
ret = __raw_readl(base + SPI_REG_RDS);
__raw_writel(out, base + SPI_REG_IOC);
#endif
}
#ifndef PER_BIT_READ
ret = __raw_readl(base + SPI_REG_RDS);
#endif
return ret;
}
static int ar71xx_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct ar71xx_spi *sp;
struct ar71xx_spi_platform_data *pdata;
struct resource *r;
int ret;
master = spi_alloc_master(&pdev->dev, sizeof(*sp));
if (master == NULL) {
dev_err(&pdev->dev, "failed to allocate spi master\n");
return -ENOMEM;
}
sp = spi_master_get_devdata(master);
platform_set_drvdata(pdev, sp);
pdata = pdev->dev.platform_data;
sp->bitbang.master = spi_master_get(master);
sp->bitbang.chipselect = ar71xx_spi_chipselect;
sp->bitbang.txrx_word[SPI_MODE_0] = ar71xx_spi_txrx_mode0;
sp->get_ioc_base = ar71xx_spi_get_ioc_base;
if (pdata) {
sp->bitbang.master->bus_num = pdata->bus_num;
sp->bitbang.master->num_chipselect = pdata->num_chipselect;
if (pdata->get_ioc_base)
sp->get_ioc_base = pdata->get_ioc_base;
} else {
sp->bitbang.master->bus_num = 0;
sp->bitbang.master->num_chipselect = 3;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL) {
ret = -ENOENT;
goto err1;
}
sp->base = ioremap_nocache(r->start, r->end - r->start + 1);
if (!sp->base) {
ret = -ENXIO;
goto err1;
}
sp->reg_ctrl = ar71xx_spi_rr(sp, SPI_REG_IOC);
ret = spi_bitbang_start(&sp->bitbang);
if (!ret)
return 0;
iounmap(sp->base);
err1:
platform_set_drvdata(pdev, NULL);
spi_master_put(sp->bitbang.master);
return ret;
}
static int ar71xx_spi_remove(struct platform_device *pdev)
{
struct ar71xx_spi *sp = platform_get_drvdata(pdev);
spi_bitbang_stop(&sp->bitbang);
iounmap(sp->base);
platform_set_drvdata(pdev, NULL);
spi_master_put(sp->bitbang.master);
return 0;
}
static struct platform_driver ar71xx_spi_drv = {
.probe = ar71xx_spi_probe,
.remove = ar71xx_spi_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};
static int __init ar71xx_spi_init(void)
{
printk(KERN_INFO DRV_DESC " version " DRV_VERSION "\n");
return platform_driver_register(&ar71xx_spi_drv);
}
module_init(ar71xx_spi_init);
static void __exit ar71xx_spi_exit(void)
{
platform_driver_unregister(&ar71xx_spi_drv);
}
module_exit(ar71xx_spi_exit);
MODULE_ALIAS("platform:" DRV_NAME);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,193 @@
/*
* EHCI HCD (Host Controller Driver) for USB.
*
* Copyright (C) 2007 Atheros Communications, Inc.
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Bus Glue for Atheros AR71xx built-in EHCI controller
*
*/
#include <linux/platform_device.h>
#include <linux/delay.h>
extern int usb_disabled(void);
static void ar71xx_start_ehci(struct platform_device *pdev)
{
}
static void ar71xx_stop_ehci(struct platform_device *pdev)
{
/*
* TODO: implement
*/
}
int usb_ehci_ar71xx_probe(const struct hc_driver *driver,
struct usb_hcd **hcd_out,
struct platform_device *pdev)
{
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
struct resource *res;
int irq;
int ret;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_dbg(&pdev->dev, "no IRQ specified for %s\n",
pdev->dev.bus_id);
return -ENODEV;
}
irq = res->start;
hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
if (!hcd)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_dbg(&pdev->dev, "no base address specified for %s\n",
pdev->dev.bus_id);
ret = -ENODEV;
goto err_put_hcd;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = res->end - res->start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dev_dbg(&pdev->dev, "controller already in use\n");
ret = -EBUSY;
goto err_put_hcd;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
dev_dbg(&pdev->dev, "error mapping memory\n");
ret = -EFAULT;
goto err_release_region;
}
ar71xx_start_ehci(pdev);
#if 0
ehci = hcd_to_ehci(hcd);
ehci->caps = hcd->regs;
ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
ehci->hcs_params = readl(&ehci->caps->hcs_params);
#endif
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
if (ret)
goto err_stop_ehc;
return 0;
err_stop_ehc:
ar71xx_stop_ehci(pdev);
iounmap(hcd->regs);
err_release_region:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put_hcd:
usb_put_hcd(hcd);
return ret;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_ehci_ar71xx_remove - shutdown processing for AR71xx-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_ar71xx_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
static void usb_ehci_ar71xx_remove(struct usb_hcd *hcd,
struct platform_device *pdev)
{
usb_remove_hcd(hcd);
ar71xx_stop_ehci(pdev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
static const struct hc_driver ehci_ar71xx_hc_driver = {
.description = hcd_name,
.product_desc = "Atheros AR71xx built-in EHCI controller",
.hcd_priv_size = sizeof(struct ehci_hcd),
/*
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
/*
* basic lifecycle operations
*/
.reset = ehci_init,
.start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ehci_get_frame,
/*
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
#ifdef CONFIG_PM
.hub_suspend = ehci_hub_suspend,
.hub_resume = ehci_hub_resume,
#endif
};
static int ehci_hcd_ar71xx_drv_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd = NULL;
int ret;
ret = -ENODEV;
if (!usb_disabled())
ret = usb_ehci_ar71xx_probe(&ehci_ar71xx_hc_driver, &hcd, pdev);
return ret;
}
static int ehci_hcd_ar71xx_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_ehci_ar71xx_remove(hcd, pdev);
return 0;
}
static struct platform_driver ehci_hcd_ar71xx_driver = {
.probe = ehci_hcd_ar71xx_drv_probe,
.remove = ehci_hcd_ar71xx_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ar71xx-ehci",
.bus = &platform_bus_type
}
};
MODULE_ALIAS("ar71xx-ehci");

View file

@ -0,0 +1,200 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* Copyright (C) 2007 Atheros Communications, Inc.
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Bus Glue for Atheros AR71xx built-in OHCI controller
*
*/
#include <linux/platform_device.h>
#include <linux/delay.h>
extern int usb_disabled(void);
static void ar71xx_start_ohci(struct platform_device *pdev)
{
}
static void ar71xx_stop_ohci(struct platform_device *pdev)
{
/*
* TODO: implement
*/
}
int usb_hcd_ar71xx_probe(const struct hc_driver *driver,
struct platform_device *pdev)
{
struct usb_hcd *hcd;
struct resource *res;
int irq;
int ret;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_dbg(&pdev->dev, "no IRQ specified for %s\n",
pdev->dev.bus_id);
return -ENODEV;
}
irq = res->start;
hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
if (!hcd)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_dbg(&pdev->dev, "no base address specified for %s\n",
pdev->dev.bus_id);
ret = -ENODEV;
goto err_put_hcd;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = res->end - res->start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dev_dbg(&pdev->dev, "controller already in use\n");
ret = -EBUSY;
goto err_put_hcd;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
dev_dbg(&pdev->dev, "error mapping memory\n");
ret = -EFAULT;
goto err_release_region;
}
ar71xx_start_ohci(pdev);
ohci_hcd_init(hcd_to_ohci(hcd));
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
if (ret)
goto err_stop_hcd;
return 0;
err_stop_hcd:
ar71xx_stop_ohci(pdev);
iounmap(hcd->regs);
err_release_region:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put_hcd:
usb_put_hcd(hcd);
return ret;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_ar71xx_remove - shutdown processing for AR71xx HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_ar71xx_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_ar71xx_remove(struct usb_hcd *hcd, struct platform_device *pdev)
{
usb_remove_hcd(hcd);
ar71xx_stop_ohci(pdev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
static int __devinit ohci_ar71xx_start(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
int ret;
ret = ohci_init(ohci);
if (ret < 0)
return ret;
ret = ohci_run(ohci);
if (ret < 0)
goto err;
return 0;
err:
ohci_stop(hcd);
return ret;
}
static const struct hc_driver ohci_ar71xx_hc_driver = {
.description = hcd_name,
.product_desc = "Atheros AR71xx built-in OHCI controller",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_ar71xx_start,
.stop = ohci_stop,
.shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
.start_port_reset = ohci_start_port_reset,
};
static int ohci_hcd_ar71xx_drv_probe(struct platform_device *pdev)
{
int ret;
ret = -ENODEV;
if (!usb_disabled())
ret = usb_hcd_ar71xx_probe(&ohci_ar71xx_hc_driver, pdev);
return ret;
}
static int ohci_hcd_ar71xx_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_hcd_ar71xx_remove(hcd, pdev);
return 0;
}
static struct platform_driver ohci_hcd_ar71xx_driver = {
.probe = ohci_hcd_ar71xx_drv_probe,
.remove = ohci_hcd_ar71xx_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ar71xx-ohci",
.owner = THIS_MODULE,
},
};
MODULE_ALIAS("ar71xx-ohci");

View file

@ -0,0 +1,375 @@
/*
* Atheros AR71xx SoC specific definitions
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef __ASM_MACH_AR71XX_H
#define __ASM_MACH_AR71XX_H
#include <linux/types.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/bitops.h>
#ifndef __ASSEMBLER__
#define AR71XX_PCI_MEM_BASE 0x10000000
#define AR71XX_PCI_MEM_SIZE 0x08000000
#define AR71XX_APB_BASE 0x18000000
#define AR71XX_GE0_BASE 0x19000000
#define AR71XX_GE0_SIZE 0x01000000
#define AR71XX_GE1_BASE 0x1a000000
#define AR71XX_GE1_SIZE 0x01000000
#define AR71XX_EHCI_BASE 0x1b000000
#define AR71XX_EHCI_SIZE 0x01000000
#define AR71XX_OHCI_BASE 0x1c000000
#define AR71XX_OHCI_SIZE 0x01000000
#define AR71XX_SPI_BASE 0x1f000000
#define AR71XX_SPI_SIZE 0x01000000
#define AR71XX_DDR_CTRL_BASE (AR71XX_APB_BASE + 0x00000000)
#define AR71XX_DDR_CTRL_SIZE 0x10000
#define AR71XX_CPU_BASE (AR71XX_APB_BASE + 0x00010000)
#define AR71XX_UART_BASE (AR71XX_APB_BASE + 0x00020000)
#define AR71XX_UART_SIZE 0x10000
#define AR71XX_USB_CTRL_BASE (AR71XX_APB_BASE + 0x00030000)
#define AR71XX_USB_CTRL_SIZE 0x10000
#define AR71XX_GPIO_BASE (AR71XX_APB_BASE + 0x00040000)
#define AR71XX_GPIO_SIZE 0x10000
#define AR71XX_PLL_BASE (AR71XX_APB_BASE + 0x00050000)
#define AR71XX_PLL_SIZE 0x10000
#define AR71XX_RESET_BASE (AR71XX_APB_BASE + 0x00060000)
#define AR71XX_RESET_SIZE 0x10000
#define AR71XX_MII_BASE (AR71XX_APB_BASE + 0x00070000)
#define AR71XX_MII_SIZE 0x10000
#define AR71XX_SLIC_BASE (AR71XX_APB_BASE + 0x00090000)
#define AR71XX_SLIC_SIZE 0x10000
#define AR71XX_DMA_BASE (AR71XX_APB_BASE + 0x000A0000)
#define AR71XX_DMA_SIZE 0x10000
#define AR71XX_STEREO_BASE (AR71XX_APB_BASE + 0x000B0000)
#define AR71XX_STEREO_SIZE 0x10000
#define AR71XX_CPU_IRQ_BASE 0
#define AR71XX_MISC_IRQ_BASE 8
#define AR71XX_MISC_IRQ_COUNT 8
#define AR71XX_GPIO_IRQ_BASE 16
#define AR71XX_GPIO_IRQ_COUNT 16
#define AR71XX_PCI_IRQ_BASE 32
#define AR71XX_PCI_IRQ_COUNT 4
#define AR71XX_CPU_IRQ_PCI (AR71XX_CPU_IRQ_BASE + 2)
#define AR71XX_CPU_IRQ_USB (AR71XX_CPU_IRQ_BASE + 3)
#define AR71XX_CPU_IRQ_GE0 (AR71XX_CPU_IRQ_BASE + 4)
#define AR71XX_CPU_IRQ_GE1 (AR71XX_CPU_IRQ_BASE + 5)
#define AR71XX_CPU_IRQ_MISC (AR71XX_CPU_IRQ_BASE + 6)
#define AR71XX_CPU_IRQ_TIMER (AR71XX_CPU_IRQ_BASE + 7)
#define AR71XX_MISC_IRQ_TIMER (AR71XX_MISC_IRQ_BASE + 0)
#define AR71XX_MISC_IRQ_ERROR (AR71XX_MISC_IRQ_BASE + 1)
#define AR71XX_MISC_IRQ_GPIO (AR71XX_MISC_IRQ_BASE + 2)
#define AR71XX_MISC_IRQ_UART (AR71XX_MISC_IRQ_BASE + 3)
#define AR71XX_MISC_IRQ_WDOG (AR71XX_MISC_IRQ_BASE + 4)
#define AR71XX_MISC_IRQ_PERFC (AR71XX_MISC_IRQ_BASE + 5)
#define AR71XX_MISC_IRQ_OHCI (AR71XX_MISC_IRQ_BASE + 6)
#define AR71XX_MISC_IRQ_DMA (AR71XX_MISC_IRQ_BASE + 7)
#define AR71XX_GPIO_IRQ(_x) (AR71XX_GPIO_IRQ_BASE + (_x))
#define AR71XX_PCI_IRQ_DEV0 (AR71XX_PCI_IRQ_BASE + 0)
#define AR71XX_PCI_IRQ_DEV1 (AR71XX_PCI_IRQ_BASE + 1)
#define AR71XX_PCI_IRQ_DEV2 (AR71XX_PCI_IRQ_BASE + 2)
#define AR71XX_PCI_IRQ_CORE (AR71XX_PCI_IRQ_BASE + 3)
extern u32 ar71xx_ahb_freq;
extern u32 ar71xx_cpu_freq;
extern u32 ar71xx_ddr_freq;
/*
* PLL block
*/
#define PLL_REG_CPU_PLL_CFG 0x00
#define PLL_REG_SEC_PLL_CFG 0x04
#define PLL_REG_CPU_CLK_CTRL 0x08
#define PLL_REG_ETH_INT0_CLK 0x10
#define PLL_REG_ETH_INT1_CLK 0x14
#define PLL_REG_ETH_EXT_CLK 0x18
#define PLL_REG_PCI_CLK 0x1c
#define PLL_DIV_SHIFT 3
#define PLL_DIV_MASK 0x1f
#define CPU_DIV_SHIFT 16
#define CPU_DIV_MASK 0x3
#define DDR_DIV_SHIFT 18
#define DDR_DIV_MASK 0x3
#define AHB_DIV_SHIFT 20
#define AHB_DIV_MASK 0x7
extern void __iomem *ar71xx_pll_base;
static inline void ar71xx_pll_wr(unsigned reg, u32 val)
{
__raw_writel(val, ar71xx_pll_base + reg);
}
static inline u32 ar71xx_pll_rr(unsigned reg)
{
return __raw_readl(ar71xx_pll_base + reg);
}
/*
* USB_CONFIG block
*/
#define USB_CTRL_REG_FLADJ 0x00
#define USB_CTRL_REG_CONFIG 0x04
extern void __iomem *ar71xx_usb_ctrl_base;
static inline void ar71xx_usb_ctrl_wr(unsigned reg, u32 val)
{
__raw_writel(val, ar71xx_usb_ctrl_base + reg);
}
static inline u32 ar71xx_usb_ctrl_rr(unsigned reg)
{
return __raw_readl(ar71xx_usb_ctrl_base + reg);
}
extern void ar71xx_add_device_usb(void) __init;
/*
* GPIO block
*/
#define GPIO_REG_OE 0x00
#define GPIO_REG_IN 0x04
#define GPIO_REG_OUT 0x08
#define GPIO_REG_SET 0x0c
#define GPIO_REG_CLEAR 0x10
#define GPIO_REG_INT_MODE 0x14
#define GPIO_REG_INT_TYPE 0x18
#define GPIO_REG_INT_POLARITY 0x1c
#define GPIO_REG_INT_PENDING 0x20
#define GPIO_REG_INT_ENABLE 0x24
#define GPIO_REG_FUNC 0x28
#define GPIO_FUNC_STEREO_EN BIT(17)
#define GPIO_FUNC_SLIC_EN BIT(16)
#define GPIO_FUNC_SPI_CS1_EN BIT(15)
#define GPIO_FUNC_SPI_CS0_EN BIT(14)
#define GPIO_FUNC_SPI_EN BIT(13)
#define GPIO_FUNC_UART_EN BIT(8)
#define GPIO_FUNC_USB_OC_EN BIT(4)
#define GPIO_FUNC_USB_CLK_EN BIT(0)
#define AR71XX_GPIO_COUNT 16
extern void __iomem *ar71xx_gpio_base;
static inline void ar71xx_gpio_wr(unsigned reg, u32 value)
{
__raw_writel(value, ar71xx_gpio_base + reg);
}
static inline u32 ar71xx_gpio_rr(unsigned reg)
{
return __raw_readl(ar71xx_gpio_base + reg);
}
extern void ar71xx_gpio_init(void) __init;
extern void ar71xx_gpio_function_enable(u32 mask);
extern void ar71xx_gpio_function_disable(u32 mask);
/*
* DDR_CTRL block
*/
#define DDR_REG_PCI_WIN0 0x7c
#define DDR_REG_PCI_WIN1 0x80
#define DDR_REG_PCI_WIN2 0x84
#define DDR_REG_PCI_WIN3 0x88
#define DDR_REG_PCI_WIN4 0x8c
#define DDR_REG_PCI_WIN5 0x90
#define DDR_REG_PCI_WIN6 0x94
#define DDR_REG_PCI_WIN7 0x98
#define DDR_REG_FLUSH_GE0 0x9c
#define DDR_REG_FLUSH_GE1 0xa0
#define DDR_REG_FLUSH_USB 0xa4
#define DDR_REG_FLUSH_PCI 0xa8
#define PCI_WIN0_OFFS 0x10000000
#define PCI_WIN1_OFFS 0x11000000
#define PCI_WIN2_OFFS 0x12000000
#define PCI_WIN3_OFFS 0x13000000
#define PCI_WIN4_OFFS 0x14000000
#define PCI_WIN5_OFFS 0x15000000
#define PCI_WIN6_OFFS 0x16000000
#define PCI_WIN7_OFFS 0x07000000
extern void __iomem *ar71xx_ddr_base;
static inline void ar71xx_ddr_wr(unsigned reg, u32 val)
{
__raw_writel(val, ar71xx_ddr_base + reg);
}
static inline u32 ar71xx_ddr_rr(unsigned reg)
{
return __raw_readl(ar71xx_ddr_base + reg);
}
extern void ar71xx_ddr_flush(u32 reg);
/*
* PCI block
*/
#define AR71XX_PCI_CFG_BASE (AR71XX_PCI_MEM_BASE + PCI_WIN7_OFFS + 0x10000)
#define AR71XX_PCI_CFG_SIZE 0x100
#define PCI_REG_CRP_AD_CBE 0x00
#define PCI_REG_CRP_WRDATA 0x04
#define PCI_REG_CRP_RDDATA 0x08
#define PCI_REG_CFG_AD 0x0c
#define PCI_REG_CFG_CBE 0x10
#define PCI_REG_CFG_WRDATA 0x14
#define PCI_REG_CFG_RDDATA 0x18
#define PCI_REG_PCI_ERR 0x1c
#define PCI_REG_PCI_ERR_ADDR 0x20
#define PCI_REG_AHB_ERR 0x24
#define PCI_REG_AHB_ERR_ADDR 0x28
#define PCI_CRP_CMD_WRITE 0x00010000
#define PCI_CRP_CMD_READ 0x00000000
#define PCI_CFG_CMD_READ 0x0000000a
#define PCI_CFG_CMD_WRITE 0x0000000b
#define PCI_IDSEL_ADL_START 17
/*
* RESET block
*/
#define RESET_REG_TIMER 0x00
#define RESET_REG_TIMER_RELOAD 0x04
#define RESET_REG_WDOG_CTRL 0x08
#define RESET_REG_WDOG 0x0c
#define RESET_REG_MISC_INT_STATUS 0x10
#define RESET_REG_MISC_INT_ENABLE 0x14
#define RESET_REG_PCI_INT_STATUS 0x18
#define RESET_REG_PCI_INT_ENABLE 0x1c
#define RESET_REG_GLOBAL_INT_STATUS 0x20
#define RESET_REG_RESET_MODULE 0x24
#define RESET_REG_PERFC_CTRL 0x2c
#define RESET_REG_PERFC0 0x30
#define RESET_REG_PERFC1 0x34
#define RESET_REG_REV_ID 0x90
#define MISC_INT_DMA BIT(7)
#define MISC_INT_OHCI BIT(6)
#define MISC_INT_PERFC BIT(5)
#define MISC_INT_WDOG BIT(4)
#define MISC_INT_UART BIT(3)
#define MISC_INT_GPIO BIT(2)
#define MISC_INT_ERROR BIT(1)
#define MISC_INT_TIMER BIT(0)
#define PCI_INT_CORE BIT(4)
#define PCI_INT_DEV2 BIT(2)
#define PCI_INT_DEV1 BIT(1)
#define PCI_INT_DEV0 BIT(0)
#define RESET_MODULE_EXTERNAL BIT(28)
#define RESET_MODULE_FULL_CHIP BIT(24)
#define RESET_MODULE_CPU_NMI BIT(21)
#define RESET_MODULE_CPU_COLD BIT(20)
#define RESET_MODULE_DMA BIT(19)
#define RESET_MODULE_SLIC BIT(18)
#define RESET_MODULE_STEREO BIT(17)
#define RESET_MODULE_DDR BIT(16)
#define RESET_MODULE_GE1_MAC BIT(13)
#define RESET_MODULE_GE1_PHY BIT(12)
#define RESET_MODULE_USBSUS_OVERRIDE BIT(10)
#define RESET_MODULE_GE0_MAC BIT(9)
#define RESET_MODULE_GE0_PHY BIT(8)
#define RESET_MODULE_USB_OHCI_DLL BIT(6)
#define RESET_MODULE_USB_HOST BIT(5)
#define RESET_MODULE_USB_PHY BIT(4)
#define RESET_MODULE_PCI_BUS BIT(1)
#define RESET_MODULE_PCI_CORE BIT(0)
#define REV_ID_MASK 0xff
#define REV_ID_CHIP_MASK 0xf3
#define REV_ID_CHIP_AR7130 0xa0
#define REV_ID_CHIP_AR7141 0xa1
#define REV_ID_CHIP_AR7161 0xa2
#define REV_ID_REVISION_MASK 0x3
#define REV_ID_REVISION_SHIFT 2
extern void __iomem *ar71xx_reset_base;
static inline void ar71xx_reset_wr(unsigned reg, u32 val)
{
__raw_writel(val, ar71xx_reset_base + reg);
}
static inline u32 ar71xx_reset_rr(unsigned reg)
{
return __raw_readl(ar71xx_reset_base + reg);
}
extern void ar71xx_device_stop(u32 mask);
extern void ar71xx_device_start(u32 mask);
/*
* SPI block
*/
#define SPI_REG_FS 0x00 /* Function Select */
#define SPI_REG_CTRL 0x04 /* SPI Control */
#define SPI_REG_IOC 0x08 /* SPI I/O Control */
#define SPI_REG_RDS 0x0c /* Read Data Shift */
#define SPI_FS_GPIO BIT(0) /* Enable GPIO mode */
#define SPI_CTRL_RD BIT(6) /* Remap Disable */
#define SPI_CTRL_DIV_MASK 0x3f
#define SPI_IOC_DO BIT(0) /* Data Out pin */
#define SPI_IOC_CLK BIT(8) /* CLK pin */
#define SPI_IOC_CS(n) BIT(16 + (n))
#define SPI_IOC_CS0 SPI_IOC_CS(0)
#define SPI_IOC_CS1 SPI_IOC_CS(1)
#define SPI_IOC_CS2 SPI_IOC_CS(2)
#define SPI_IOC_CS_ALL (SPI_IOC_CS0 | SPI_IOC_CS1 | SPI_IOC_CS2)
/*
* MII_CTRL block
*/
#define MII_REG_MII0_CTRL 0x00
#define MII_REG_MII1_CTRL 0x04
#define MII0_CTRL_IF_GMII 0
#define MII0_CTRL_IF_MII 1
#define MII0_CTRL_IF_RGMII 2
#define MII0_CTRL_IF_RMII 3
#define MII1_CTRL_IF_RGMII 0
#define MII1_CTRL_IF_RMII 1
#include <asm/bootinfo.h>
#include <linux/init.h>
#define ar71xx_print_cmdline() do { \
printk(KERN_DEBUG "%s:%d arcs_cmdline:'%s'\n", \
__FUNCTION__, __LINE__, arcs_cmdline); \
printk(KERN_DEBUG "%s:%d boot_command_line:'%s'\n", \
__FUNCTION__, __LINE__, boot_command_line); \
} while (0)
#endif /* __ASSEMBLER__ */
#endif /* __ASM_MACH_AR71XX_H */

View file

@ -0,0 +1,56 @@
/*
* Atheros AR71xx specific CPU feature overrides
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This file was derived from: include/asm-mips/cpu-features.h
* Copyright (C) 2003, 2004 Ralf Baechle
* Copyright (C) 2004 Maciej W. Rozycki
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
*/
#ifndef __ASM_MACH_AR71XX_CPU_FEATURE_OVERRIDES_H
#define __ASM_MACH_AR71XX_CPU_FEATURE_OVERRIDES_H
#define cpu_has_tlb 1
#define cpu_has_4kex 1
#define cpu_has_3k_cache 0
#define cpu_has_4k_cache 1
#define cpu_has_tx39_cache 0
#define cpu_has_sb1_cache 0
#define cpu_has_fpu 0
#define cpu_has_32fpr 0
#define cpu_has_counter 1
#define cpu_has_watch 1
#define cpu_has_divec 1
#define cpu_has_prefetch 1
#define cpu_has_ejtag 1
#define cpu_has_llsc 1
#define cpu_has_mips16 1
#define cpu_has_mdmx 0
#define cpu_has_mips3d 0
#define cpu_has_smartmips 0
#define cpu_has_mips32r1 1
#define cpu_has_mips32r2 1
#define cpu_has_mips64r1 0
#define cpu_has_mips64r2 0
#define cpu_has_dsp 0
#define cpu_has_mipsmt 0
#define cpu_has_64bits 0
#define cpu_has_64bit_zero_reg 0
#define cpu_has_64bit_gp_regs 0
#define cpu_has_64bit_addresses 0
#define cpu_dcache_line_size() 32
#define cpu_icache_line_size() 32
#endif /* __ASM_MACH_AR71XX_CPU_FEATURE_OVERRIDES_H */

View file

@ -0,0 +1,52 @@
/*
* Atheros AR71xx GPIO API definitions
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
*/
#ifndef __ASM_MACH_AR71XX_GPIO_H
#define __ASM_MACH_AR71XX_GPIO_H
#define ARCH_NR_GPIOS 64
#include <asm-generic/gpio.h>
#include <asm/mach-ar71xx/ar71xx.h>
extern void __ar71xx_gpio_set_value(unsigned gpio, int value);
extern int __ar71xx_gpio_get_value(unsigned gpio);
static inline int gpio_to_irq(unsigned gpio)
{
return AR71XX_GPIO_IRQ(gpio);
}
static inline int irq_to_gpio(unsigned irq)
{
return irq - AR71XX_GPIO_IRQ_BASE;
}
static inline int gpio_get_value(unsigned gpio)
{
if (gpio < AR71XX_GPIO_COUNT)
return __ar71xx_gpio_get_value(gpio);
return __gpio_get_value(gpio);
}
static inline void gpio_set_value(unsigned gpio, int value)
{
if (gpio < AR71XX_GPIO_COUNT)
__ar71xx_gpio_set_value(gpio, value);
else
__gpio_set_value(gpio, value);
}
#define gpio_cansleep __gpio_cansleep
#endif /* __ASM_MACH_AR71XX_GPIO_H */

View file

@ -0,0 +1,17 @@
/*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef __ASM_MACH_AR71XX_IRQ_H
#define __ASM_MACH_AR71XX_IRQ_H
#define MIPS_CPU_IRQ_BASE 0
#define NR_IRQS 36
#include_next <irq.h>
#endif /* __ASM_MACH_AR71XX_IRQ_H */

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This file was derived from: inlude/asm-mips/mach-generic/mangle-port.h
* Copyright (C) 2003, 2004 Ralf Baechle
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef __ASM_MACH_AR71XX_MANGLE_PORT_H
#define __ASM_MACH_AR71XX_MANGLE_PORT_H
#define __swizzle_addr_b(port) ((port) ^ 3)
#define __swizzle_addr_w(port) ((port) ^ 2)
#define __swizzle_addr_l(port) (port)
#define __swizzle_addr_q(port) (port)
#if defined(CONFIG_SWAP_IO_SPACE)
# define ioswabb(a, x) (x)
# define __mem_ioswabb(a, x) (x)
# define ioswabw(a, x) le16_to_cpu(x)
# define __mem_ioswabw(a, x) (x)
# define ioswabl(a, x) le32_to_cpu(x)
# define __mem_ioswabl(a, x) (x)
# define ioswabq(a, x) le64_to_cpu(x)
# define __mem_ioswabq(a, x) (x)
#else
# define ioswabb(a, x) (x)
# define __mem_ioswabb(a, x) (x)
# define ioswabw(a, x) (x)
# define __mem_ioswabw(a, x) cpu_to_le16(x)
# define ioswabl(a, x) (x)
# define __mem_ioswabl(a, x) cpu_to_le32(x)
# define ioswabq(a, x) (x)
# define __mem_ioswabq(a, x) cpu_to_le64(x)
#endif
#endif /* __ASM_MACH_AR71XX_MANGLE_PORT_H */

View file

@ -0,0 +1,28 @@
/*
* Atheros AR71xx SoC specific PCI definitions
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef __ASM_MACH_AR71XX_PCI_H
#define __ASM_MACH_AR71XX_PCI_H
struct ar71xx_pci_irq {
int irq;
u8 slot;
u8 pin;
};
extern int (*ar71xx_pci_be_handler)(int is_fixup);
extern int (*ar71xx_pci_bios_init)(unsigned nr_irqs,
struct ar71xx_pci_irq *map) __initdata;
extern int ar71xx_pci_init(unsigned nr_irqs,
struct ar71xx_pci_irq *map) __init;
#endif /* __ASM_MACH_AR71XX_PCI_H */

View file

@ -0,0 +1,42 @@
/*
* Atheros AR71xx SoC specific platform definitions
*
* Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef __ASM_MACH_AR71XX_PLATFORM_H
#define __ASM_MACH_AR71XX_PLATFORM_H
#include <linux/skbuff.h>
#include <linux/phy.h>
#include <linux/spi/spi.h>
struct ag71xx_platform_data {
u32 reset_bit;
u32 flush_reg;
u32 phy_mask;
phy_interface_t phy_if_mode;
u32 mii_if;
};
struct ar71xx_spi_platform_data {
unsigned bus_num;
unsigned num_chipselect;
u32 (*get_ioc_base)(u8 chip_select, int cs_high, int is_on);
};
#define AR71XX_SPI_CS_INACTIVE 0
#define AR71XX_SPI_CS_ACTIVE 1
extern void ar71xx_add_device_spi(struct ar71xx_spi_platform_data *pdata,
struct spi_board_info const *info,
unsigned n) __init;
extern void ar71xx_add_device_eth(unsigned int id, phy_interface_t phy_if_mode,
u32 phy_mask) __init;
#endif /* __ASM_MACH_AR71XX_PLATFORM_H */

View file

@ -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_MACH_AR71XX_WAR_H
#define __ASM_MACH_AR71XX_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_MACH_AR71XX_WAR_H */

View file

@ -0,0 +1,28 @@
#
# Copyright (C) 2008 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/image.mk
define Image/BuildKernel
cp $(KDIR)/vmlinux.elf $(BIN_DIR)/openwrt-$(BOARD)-vmlinux.elf
gzip -9 -c $(KDIR)/vmlinux > $(KDIR)/vmlinux.bin.gz
$(STAGING_DIR_HOST)/bin/lzma e $(KDIR)/vmlinux $(KDIR)/vmlinux.bin.l7
dd if=$(KDIR)/vmlinux.bin.l7 of=$(BIN_DIR)/openwrt-$(BOARD)-vmlinux.lzma bs=65536 conv=sync
dd if=$(KDIR)/vmlinux.bin.gz of=$(BIN_DIR)/openwrt-$(BOARD)-vmlinux.gz bs=65536 conv=sync
endef
define Image/Build/squashfs
$(call prepare_generic_squashfs,$(KDIR)/root.squashfs)
endef
define Image/Build
$(call Image/Build/$(1))
dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-root.$(1) bs=128k conv=sync
endef
$(eval $(call BuildImage))

View file

@ -0,0 +1,61 @@
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -593,6 +593,13 @@
cflags-$(CONFIG_TOSHIBA_RBTX4938) += -Iinclude/asm-mips/mach-tx49xx
load-$(CONFIG_TOSHIBA_RBTX4938) += 0xffffffff80100000
+#
+# Atheros AR71xx
+#
+core-$(CONFIG_ATHEROS_AR71XX) += arch/mips/ar71xx/
+cflags-$(CONFIG_ATHEROS_AR71XX) += -Iinclude/asm-mips/mach-ar71xx
+load-$(CONFIG_ATHEROS_AR71XX) += 0xffffffff80060000
+
# temporary until string.h is fixed
cflags-y += -ffreestanding
--- a/include/asm-mips/bootinfo.h
+++ b/include/asm-mips/bootinfo.h
@@ -79,6 +79,15 @@
#define MACH_LASAT_200 1 /* Masquerade PRO/SP200 */
/*
+ * Valid machtype for Atheros AR71xx based boards
+ */
+#define MACH_AR71XX_GENERIC 0
+#define MACH_AR71XX_WP543 1 /* Compex WP543 */
+#define MACH_AR71XX_RB_411 2 /* MikroTik RouterBOARD 411 */
+#define MACH_AR71XX_RB_433 3 /* MikroTik RouterBOARD 433/433AH */
+#define MACH_AR71XX_RB_450 4 /* MikroTik RouterBOARD 450 */
+
+/*
* Valid machtype for group NEC EMMA2RH
*/
#define MACH_NEC_MARKEINS 0 /* NEC EMMA2RH Mark-eins */
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -21,6 +21,24 @@
config MACH_ALCHEMY
bool "Alchemy processor based machines"
+config ATHEROS_AR71XX
+ bool "Atheros AR71xx based boards"
+ select CEVT_R4K
+ select CSRC_R4K
+ select DMA_NONCOHERENT
+ select HW_HAS_PCI
+ select IRQ_CPU
+ select GENERIC_GPIO
+ select HAVE_GPIO_LIB
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_HAS_CPU_MIPS32_R2
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select SYS_SUPPORTS_BIG_ENDIAN
+ select SYS_HAS_EARLY_PRINTK
+ select MIPS_MACHINE
+ help
+ Support for Atheros AR71xx based boards.
+
config BASLER_EXCITE
bool "Basler eXcite smart camera"
select CEVT_R4K

View file

@ -0,0 +1,10 @@
--- a/arch/mips/pci/Makefile
+++ b/arch/mips/pci/Makefile
@@ -15,6 +15,7 @@
obj-$(CONFIG_PCI_VR41XX) += ops-vr41xx.o pci-vr41xx.o
obj-$(CONFIG_NEC_CMBVR4133) += fixup-vr4133.o
obj-$(CONFIG_MARKEINS) += ops-emma2rh.o pci-emma2rh.o fixup-emma2rh.o
+obj-$(CONFIG_ATHEROS_AR71XX) += pci-ar71xx.o
#
# These are still pretty much in the old state, watch, go blind.

View file

@ -0,0 +1,60 @@
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -260,3 +260,15 @@
To compile this driver as a module, choose M here: the
module will be called r8a66597-hcd.
+config USB_OHCI_AR71XX
+ bool "USB OHCI support for Atheros AR71xx"
+ depends on ATHEROS_AR71XX && USB_OHCI_HCD
+ help
+ Support for Atheros AR71xx built-in OHCI controller
+
+config USB_EHCI_AR71XX
+ bool "USB EHCI support for AR71xx"
+ depends on ATHEROS_AR71XX && USB_EHCI_HCD
+ help
+ Support for Atheros AR71xx built-in EHCI controller
+
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1043,6 +1043,11 @@
#define PLATFORM_DRIVER ixp4xx_ehci_driver
#endif
+#ifdef CONFIG_USB_EHCI_AR71XX
+#include "ehci-ar71xx.c"
+#define PLATFORM_DRIVER ehci_hcd_ar71xx_driver
+#endif
+
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER)
#error "missing bus glue for ehci-hcd"
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -538,6 +538,11 @@
#define writel_be(val, addr) out_be32((__force unsigned *)addr, val)
#endif
+#if defined(CONFIG_ATHEROS_AR71XX)
+#define readl_be(addr) __raw_readl(addr)
+#define writel_be(val, addr) __raw_writel(addr, val)
+#endif
+
static inline unsigned int _ohci_readl (const struct ohci_hcd *ohci,
__hc32 __iomem * regs)
{
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1057,6 +1057,11 @@
#define PLATFORM_DRIVER ohci_hcd_sm501_driver
#endif
+#ifdef CONFIG_USB_OHCI_AR71XX
+#include "ohci-ar71xx.c"
+#define PLATFORM_DRIVER ohci_hcd_ar71xx_driver
+#endif
+
#if !defined(PCI_DRIVER) && \
!defined(PLATFORM_DRIVER) && \
!defined(OF_PLATFORM_DRIVER) && \

View file

@ -0,0 +1,26 @@
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -52,6 +52,13 @@
comment "SPI Master Controller Drivers"
depends on SPI_MASTER
+config SPI_AR71XX
+ tristate "Atheros AR71xx SPI Controller"
+ depends on SPI_MASTER && ATHEROS_AR71XX
+ select SPI_BITBANG
+ help
+ This is the SPI contoller driver for Atheros AR71xx.
+
config SPI_ATMEL
tristate "Atmel SPI Controller"
depends on (ARCH_AT91 || AVR32) && SPI_MASTER
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -11,6 +11,7 @@
obj-$(CONFIG_SPI_MASTER) += spi.o
# SPI master controller drivers (bus)
+obj-$(CONFIG_SPI_AR71XX) += ar71xx_spi.o
obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o
obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o
obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o

View file

@ -0,0 +1,21 @@
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2067,6 +2067,8 @@
The safe and default value for this is N.
+source drivers/net/ag71xx/Kconfig
+
config DL2K
tristate "DL2000/TC902x-based Gigabit Ethernet support"
depends on PCI
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -2,6 +2,7 @@
# Makefile for the Linux network (ethercard) device drivers.
#
+obj-$(CONFIG_AG71XX) += ag71xx/
obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_E1000E) += e1000e/
obj-$(CONFIG_IBM_EMAC) += ibm_emac/

View file

@ -0,0 +1,13 @@
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -441,6 +441,10 @@
{ "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, },
{ "at26df321", 0x1f4701, 64 * 1024, 64, SECT_4K, },
+ /* PMC -- pm25x "blocks" are 32K, sectors are 4K */
+ { "pm25lv512", 0, 32 * 1024, 2, SECT_4K },
+ { "pm25lv010", 0, 32 * 1024, 4, SECT_4K },
+
/* Spansion -- single (large) sector size only, at least
* for the chips listed here (without boot sectors).
*/

View file

@ -0,0 +1,23 @@
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -76,6 +76,11 @@
---help---
Currently supports the Marvell 88E6060 switch.
+config MICREL_PHY
+ tristate "Drivers for Micrel/Kendin PHYs"
+ ---help---
+ Currently has a driver for the KSZ8041
+
config FIXED_PHY
bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs"
depends on PHYLIB=y
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -15,5 +15,6 @@
obj-$(CONFIG_ADM6996_PHY) += adm6996.o
obj-$(CONFIG_MVSWITCH_PHY) += mvswitch.o
obj-$(CONFIG_REALTEK_PHY) += realtek.o
+obj-$(CONFIG_MICREL_PHY) += micrel.o
obj-$(CONFIG_FIXED_PHY) += fixed.o
obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o

View file

@ -0,0 +1,54 @@
--- a/drivers/mtd/redboot.c
+++ b/drivers/mtd/redboot.c
@@ -59,31 +59,32 @@
static char nullstring[] = "unallocated";
#endif
+ buf = vmalloc(master->erasesize);
+ if (!buf)
+ return -ENOMEM;
+
+ restart:
if ( directory < 0 ) {
offset = master->size + directory * master->erasesize;
- while (master->block_isbad &&
+ while (master->block_isbad &&
master->block_isbad(master, offset)) {
if (!offset) {
nogood:
printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n");
+ vfree(buf);
return -EIO;
}
offset -= master->erasesize;
}
} else {
offset = directory * master->erasesize;
- while (master->block_isbad &&
+ while (master->block_isbad &&
master->block_isbad(master, offset)) {
offset += master->erasesize;
if (offset == master->size)
goto nogood;
}
}
- buf = vmalloc(master->erasesize);
-
- if (!buf)
- return -ENOMEM;
-
printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n",
master->name, offset);
@@ -155,6 +156,11 @@
}
if (i == numslots) {
/* Didn't find it */
+ if (offset + master->erasesize < master->size) {
+ /* not at the end of the flash yet, maybe next block :) */
+ directory++;
+ goto restart;
+ }
printk(KERN_NOTICE "No RedBoot partition table detected in %s\n",
master->name);
ret = 0;

View file

@ -0,0 +1,21 @@
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -330,4 +330,8 @@
Enabling this option will enable you to use this to control
external NAND devices.
+config MTD_NAND_RB4XX
+ tristate "NAND flash driver for RouterBoard 4xx series"
+ depends on MTD_NAND && ATHEROS_AR71XX
+
endif # MTD_NAND
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -28,6 +28,7 @@
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
+obj-$(CONFIG_MTD_NAND_RB4XX) += rb4xx_nand.o
obj-$(CONFIG_MTD_ALAUDA) += alauda.o
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o

View file

@ -0,0 +1,143 @@
--- /dev/null
+++ b/include/asm-mips/mips_machine.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#ifndef __ASM_MIPS_MACHINE_H
+#define __ASM_MIPS_MACHINE_H
+
+#include <linux/init.h>
+#include <linux/list.h>
+
+#include <asm/bootinfo.h>
+
+#define MIPS_MACHINE_NAME_LEN 64
+
+struct mips_machine {
+ unsigned long mach_type;
+ void (*mach_setup)(void);
+ unsigned char mach_name[MIPS_MACHINE_NAME_LEN];
+ struct list_head list;
+};
+
+void mips_machine_register(struct mips_machine *) __init;
+void mips_machine_setup(void) __init;
+
+extern unsigned char mips_machine_name[MIPS_MACHINE_NAME_LEN];
+
+#define MIPS_MACHINE(_type, _name, _setup) \
+static struct mips_machine machine_##_type __initdata = \
+{ \
+ .mach_type = _type, \
+ .mach_name = _name, \
+ .mach_setup = _setup, \
+}; \
+ \
+static int __init register_machine_##_type(void) \
+{ \
+ mips_machine_register(&machine_##_type); \
+ return 0; \
+} \
+ \
+pure_initcall(register_machine_##_type)
+
+#endif /* __ASM_MIPS_MACHINE_H */
+
--- /dev/null
+++ b/arch/mips/kernel/mips_machine.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#include <asm/mips_machine.h>
+#include <asm/bootinfo.h>
+
+static struct list_head mips_machines __initdata =
+ LIST_HEAD_INIT(mips_machines);
+
+unsigned char mips_machine_name[MIPS_MACHINE_NAME_LEN] = "Unknown";
+
+static struct mips_machine * __init mips_machine_find(unsigned long machtype)
+{
+ struct list_head *this;
+
+ list_for_each(this, &mips_machines) {
+ struct mips_machine *mach;
+
+ mach = list_entry(this, struct mips_machine, list);
+ if (mach->mach_type == machtype)
+ return mach;
+ }
+
+ return NULL;
+}
+
+void __init mips_machine_register(struct mips_machine *mach)
+{
+ list_add_tail(&mach->list, &mips_machines);
+}
+
+void __init mips_machine_setup(void)
+{
+ struct mips_machine *mach;
+
+ mach = mips_machine_find(mips_machtype);
+ if (!mach) {
+ printk(KERN_ALERT "MIPS: no machine registered for "
+ "machtype %lu\n", mips_machtype);
+ return;
+ }
+
+ if (mach->mach_name[0])
+ strncpy(mips_machine_name, mach->mach_name,
+ MIPS_MACHINE_NAME_LEN);
+
+ printk(KERN_INFO "MIPS: machine is %s\n", mips_machine_name);
+
+ if (mach->mach_setup)
+ mach->mach_setup();
+}
+
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -79,6 +79,7 @@
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+obj-$(CONFIG_MIPS_MACHINE) += mips_machine.o
CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi)
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -700,6 +700,7 @@
endchoice
+source "arch/mips/ar71xx/Kconfig"
source "arch/mips/au1000/Kconfig"
source "arch/mips/basler/excite/Kconfig"
source "arch/mips/jazz/Kconfig"
@@ -857,6 +858,9 @@
config MIPS_DISABLE_OBSOLETE_IDE
bool
+config MIPS_MACHINE
+ def_bool n
+
config NO_IOPORT
def_bool n

View file

@ -0,0 +1,29 @@
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -42,6 +42,7 @@
#include <asm/mmu_context.h>
#include <asm/types.h>
#include <asm/stacktrace.h>
+#include <asm/time.h>
extern asmlinkage void handle_int(void);
extern asmlinkage void handle_tlbm(void);
@@ -1379,6 +1380,8 @@
*/
if (cpu_has_mips_r2) {
cp0_compare_irq = (read_c0_intctl() >> 29) & 7;
+ if (get_c0_compare_irq)
+ cp0_compare_irq = get_c0_compare_irq();
cp0_perfcount_irq = (read_c0_intctl() >> 26) & 7;
if (cp0_perfcount_irq == cp0_compare_irq)
cp0_perfcount_irq = -1;
--- a/include/asm-mips/time.h
+++ b/include/asm-mips/time.h
@@ -53,6 +53,7 @@
#ifdef CONFIG_CEVT_R4K
extern int mips_clockevent_init(void);
extern unsigned int __weak get_c0_compare_int(void);
+extern unsigned int __weak get_c0_compare_irq(void);
#else
static inline int mips_clockevent_init(void)
{

View file

@ -0,0 +1,45 @@
--- a/include/asm-mips/hazards.h
+++ b/include/asm-mips/hazards.h
@@ -64,7 +64,7 @@
_ehb
)
ASMMACRO(back_to_back_c0_hazard,
- _ehb
+ _ssnop; _ssnop; _ssnop; _ehb
)
/*
* gcc has a tradition of misscompiling the previous construct using the
--- a/arch/mips/kernel/cevt-r4k.c
+++ b/arch/mips/kernel/cevt-r4k.c
@@ -187,7 +187,7 @@
*/
if (c0_compare_int_pending()) {
write_c0_compare(read_c0_count());
- irq_disable_hazard();
+ back_to_back_c0_hazard();
if (c0_compare_int_pending())
return 0;
}
@@ -196,7 +196,7 @@
cnt = read_c0_count();
cnt += delta;
write_c0_compare(cnt);
- irq_disable_hazard();
+ back_to_back_c0_hazard();
if ((int)(read_c0_count() - cnt) < 0)
break;
/* increase delta if the timer was already expired */
@@ -205,11 +205,12 @@
while ((int)(read_c0_count() - cnt) <= 0)
; /* Wait for expiry */
+ back_to_back_c0_hazard();
if (!c0_compare_int_pending())
return 0;
write_c0_compare(read_c0_count());
- irq_disable_hazard();
+ back_to_back_c0_hazard();
if (c0_compare_int_pending())
return 0;