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;

View file

@ -0,0 +1,17 @@
diff -Nur a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
--- a/drivers/mtd/devices/m25p80.c 2008-07-06 14:36:59.000000000 +0200
+++ b/drivers/mtd/devices/m25p80.c 2008-07-06 15:17:48.000000000 +0200
@@ -631,12 +631,10 @@
struct mtd_partition *parts = NULL;
int nr_parts = 0;
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- static const char *part_probes[] = { "cmdlinepart", NULL, };
+ static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL, };
nr_parts = parse_mtd_partitions(&flash->mtd,
part_probes, &parts, 0);
-#endif
if (nr_parts <= 0 && data && data->parts) {
parts = data->parts;

View file

@ -1,18 +0,0 @@
Backport gpio_is_valid() for gpiolib from linux-2.6.26
Index: linux-2.6.25.10/include/asm-generic/gpio.h
===================================================================
--- linux-2.6.25.10.orig/include/asm-generic/gpio.h 2008-07-03 05:46:47.000000000 +0200
+++ linux-2.6.25.10/include/asm-generic/gpio.h 2008-07-20 20:32:12.000000000 +0200
@@ -16,6 +16,12 @@
#define ARCH_NR_GPIOS 256
#endif
+static inline int gpio_is_valid(int number)
+{
+ /* only some non-negative numbers are valid */
+ return ((unsigned)number) < ARCH_NR_GPIOS;
+}
+
struct seq_file;
/**

View file

@ -0,0 +1,93 @@
From: Guennadi Liakhovetski <g.liakhovetski@pengutronix.de>
Date: Mon, 28 Apr 2008 09:14:44 +0000 (-0700)
Subject: gpiolib: better rmmod infrastructure
X-Git-Tag: v2.6.26-rc1~851
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=438d8908b379b6322fc3b28d45c9ebdddf58bc20
gpiolib: better rmmod infrastructure
As long as one or more GPIOs on a gpio chip are used its driver should not be
unloaded. The existing mechanism (gpiochip_remove failure) doesn't address
that, since rmmod can no longer be made to fail by having the cleanup code
report errors. Module usecounts are the solution.
Assuming standard "initialize struct to zero" policies, this change won't
affect SOC platform drivers. However, drivers for external chips (on I2C and
SPI busses) should be updated if they can be built as modules.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@pengutronix.de>
[ gpio_ensure_requested() needs to update module usecounts too ]
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index d8db2f8..eb75d12 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -68,6 +68,9 @@ static void gpio_ensure_requested(struct gpio_desc *desc)
if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
pr_warning("GPIO-%d autorequested\n", (int)(desc - gpio_desc));
desc_set_label(desc, "[auto]");
+ if (!try_module_get(desc->chip->owner))
+ pr_err("GPIO-%d: module can't be gotten \n",
+ (int)(desc - gpio_desc));
}
}
@@ -177,6 +180,9 @@ int gpio_request(unsigned gpio, const char *label)
if (desc->chip == NULL)
goto done;
+ if (!try_module_get(desc->chip->owner))
+ goto done;
+
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled.
*/
@@ -184,8 +190,10 @@ int gpio_request(unsigned gpio, const char *label)
if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
desc_set_label(desc, label ? : "?");
status = 0;
- } else
+ } else {
status = -EBUSY;
+ module_put(desc->chip->owner);
+ }
done:
if (status)
@@ -209,9 +217,10 @@ void gpio_free(unsigned gpio)
spin_lock_irqsave(&gpio_lock, flags);
desc = &gpio_desc[gpio];
- if (desc->chip && test_and_clear_bit(FLAG_REQUESTED, &desc->flags))
+ if (desc->chip && test_and_clear_bit(FLAG_REQUESTED, &desc->flags)) {
desc_set_label(desc, NULL);
- else
+ module_put(desc->chip->owner);
+ } else
WARN_ON(extra_checks);
spin_unlock_irqrestore(&gpio_lock, flags);
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index f29a502..7e77b6f 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -17,6 +17,7 @@
#endif
struct seq_file;
+struct module;
/**
* struct gpio_chip - abstract a GPIO controller
@@ -48,6 +49,7 @@ struct seq_file;
*/
struct gpio_chip {
char *label;
+ struct module *owner;
int (*direction_input)(struct gpio_chip *chip,
unsigned offset);

View file

@ -0,0 +1,136 @@
From: Guennadi Liakhovetski <g.liakhovetski@pengutronix.de>
Date: Mon, 28 Apr 2008 09:14:46 +0000 (-0700)
Subject: gpio: define gpio_is_valid()
X-Git-Tag: v2.6.26-rc1~849
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=e6de1808f8ebfeb7e49f3c5a30cb8f2032beb287
gpio: define gpio_is_valid()
Introduce a gpio_is_valid() predicate; use it in gpiolib.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@pengutronix.de>
[ use inline function; follow the gpio_* naming convention;
work without gpiolib; all programming interfaces need docs ]
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt
index 5463009..c35ca9e 100644
--- a/Documentation/gpio.txt
+++ b/Documentation/gpio.txt
@@ -107,6 +107,16 @@ type of GPIO controller, and on one particular board 80-95 with an FPGA.
The numbers need not be contiguous; either of those platforms could also
use numbers 2000-2063 to identify GPIOs in a bank of I2C GPIO expanders.
+If you want to initialize a structure with an invalid GPIO number, use
+some negative number (perhaps "-EINVAL"); that will never be valid. To
+test if a number could reference a GPIO, you may use this predicate:
+
+ int gpio_is_valid(int number);
+
+A number that's not valid will be rejected by calls which may request
+or free GPIOs (see below). Other numbers may also be rejected; for
+example, a number might be valid but unused on a given board.
+
Whether a platform supports multiple GPIO controllers is currently a
platform-specific implementation issue.
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index eb75d12..623fcd9 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -99,7 +99,7 @@ int gpiochip_add(struct gpio_chip *chip)
* dynamic allocation. We don't currently support that.
*/
- if (chip->base < 0 || (chip->base + chip->ngpio) >= ARCH_NR_GPIOS) {
+ if (chip->base < 0 || !gpio_is_valid(chip->base + chip->ngpio)) {
status = -EINVAL;
goto fail;
}
@@ -174,7 +174,7 @@ int gpio_request(unsigned gpio, const char *label)
spin_lock_irqsave(&gpio_lock, flags);
- if (gpio >= ARCH_NR_GPIOS)
+ if (!gpio_is_valid(gpio))
goto done;
desc = &gpio_desc[gpio];
if (desc->chip == NULL)
@@ -209,7 +209,7 @@ void gpio_free(unsigned gpio)
unsigned long flags;
struct gpio_desc *desc;
- if (gpio >= ARCH_NR_GPIOS) {
+ if (!gpio_is_valid(gpio)) {
WARN_ON(extra_checks);
return;
}
@@ -245,7 +245,7 @@ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
{
unsigned gpio = chip->base + offset;
- if (gpio >= ARCH_NR_GPIOS || gpio_desc[gpio].chip != chip)
+ if (!gpio_is_valid(gpio) || gpio_desc[gpio].chip != chip)
return NULL;
if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0)
return NULL;
@@ -276,7 +276,7 @@ int gpio_direction_input(unsigned gpio)
spin_lock_irqsave(&gpio_lock, flags);
- if (gpio >= ARCH_NR_GPIOS)
+ if (!gpio_is_valid(gpio))
goto fail;
chip = desc->chip;
if (!chip || !chip->get || !chip->direction_input)
@@ -314,7 +314,7 @@ int gpio_direction_output(unsigned gpio, int value)
spin_lock_irqsave(&gpio_lock, flags);
- if (gpio >= ARCH_NR_GPIOS)
+ if (!gpio_is_valid(gpio))
goto fail;
chip = desc->chip;
if (!chip || !chip->set || !chip->direction_output)
@@ -531,7 +531,7 @@ static int gpiolib_show(struct seq_file *s, void *unused)
/* REVISIT this isn't locked against gpio_chip removal ... */
- for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {
+ for (gpio = 0; gpio_is_valid(gpio); gpio++) {
if (chip == gpio_desc[gpio].chip)
continue;
chip = gpio_desc[gpio].chip;
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 7e77b6f..464c5b3 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -16,6 +16,12 @@
#define ARCH_NR_GPIOS 256
#endif
+static inline int gpio_is_valid(int number)
+{
+ /* only some non-negative numbers are valid */
+ return ((unsigned)number) < ARCH_NR_GPIOS;
+}
+
struct seq_file;
struct module;
@@ -99,6 +105,12 @@ extern int __gpio_cansleep(unsigned gpio);
#else
+static inline int gpio_is_valid(int number)
+{
+ /* only non-negative numbers are valid */
+ return number >= 0;
+}
+
/* platforms that don't directly support access to GPIOs through I2C, SPI,
* or other blocking infrastructure can use these wrappers.
*/

View file

@ -0,0 +1,118 @@
From: Anton Vorontsov <avorontsov@ru.mvista.com>
Date: Mon, 28 Apr 2008 09:14:46 +0000 (-0700)
Subject: gpiolib: dynamic gpio number allocation
X-Git-Tag: v2.6.26-rc1~848
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=8d0aab2f16c4fa170f32e7a74a52cd0122bbafef
gpiolib: dynamic gpio number allocation
If gpio_chip->base is negative during registration, gpiolib performs dynamic
base allocation. This is useful for devices that aren't always present, such
as GPIOs on hotplugged devices rather than mainboards. (This behavior was
previously specified but not implemented.)
To avoid using any numbers that may have been explicitly assigned but not yet
registered, this dynamic allocation assigns GPIO numbers from the biggest
number on down, instead of from the smallest on up.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 623fcd9..2ba6127 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -80,6 +80,33 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
return gpio_desc[gpio].chip;
}
+/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
+static int gpiochip_find_base(int ngpio)
+{
+ int i;
+ int spare = 0;
+ int base = -ENOSPC;
+
+ for (i = ARCH_NR_GPIOS - 1; i >= 0 ; i--) {
+ struct gpio_chip *chip = gpio_desc[i].chip;
+
+ if (!chip) {
+ spare++;
+ if (spare == ngpio) {
+ base = i;
+ break;
+ }
+ } else {
+ spare = 0;
+ i -= chip->ngpio - 1;
+ }
+ }
+
+ if (gpio_is_valid(base))
+ pr_debug("%s: found new base at %d\n", __func__, base);
+ return base;
+}
+
/**
* gpiochip_add() - register a gpio_chip
* @chip: the chip to register, with chip->base initialized
@@ -88,38 +115,49 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
* Returns a negative errno if the chip can't be registered, such as
* because the chip->base is invalid or already associated with a
* different chip. Otherwise it returns zero as a success code.
+ *
+ * If chip->base is negative, this requests dynamic assignment of
+ * a range of valid GPIOs.
*/
int gpiochip_add(struct gpio_chip *chip)
{
unsigned long flags;
int status = 0;
unsigned id;
+ int base = chip->base;
- /* NOTE chip->base negative is reserved to mean a request for
- * dynamic allocation. We don't currently support that.
- */
-
- if (chip->base < 0 || !gpio_is_valid(chip->base + chip->ngpio)) {
+ if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio))
+ && base >= 0) {
status = -EINVAL;
goto fail;
}
spin_lock_irqsave(&gpio_lock, flags);
+ if (base < 0) {
+ base = gpiochip_find_base(chip->ngpio);
+ if (base < 0) {
+ status = base;
+ goto fail_unlock;
+ }
+ chip->base = base;
+ }
+
/* these GPIO numbers must not be managed by another gpio_chip */
- for (id = chip->base; id < chip->base + chip->ngpio; id++) {
+ for (id = base; id < base + chip->ngpio; id++) {
if (gpio_desc[id].chip != NULL) {
status = -EBUSY;
break;
}
}
if (status == 0) {
- for (id = chip->base; id < chip->base + chip->ngpio; id++) {
+ for (id = base; id < base + chip->ngpio; id++) {
gpio_desc[id].chip = chip;
gpio_desc[id].flags = 0;
}
}
+fail_unlock:
spin_unlock_irqrestore(&gpio_lock, flags);
fail:
/* failures here can mean systems won't boot... */

View file

@ -0,0 +1,116 @@
From: Anton Vorontsov <avorontsov@ru.mvista.com>
Date: Mon, 28 Apr 2008 09:14:47 +0000 (-0700)
Subject: gpiochip_reserve()
X-Git-Tag: v2.6.26-rc1~847
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=169b6a7a6e91e1ea32136681b475cbaf2074bf35
gpiochip_reserve()
Add a new function gpiochip_reserve() to reserve ranges of gpios that platform
code has pre-allocated. That is, this marks gpio numbers which will be
claimed by drivers that haven't yet been loaded, and thus are not available
for dynamic gpio number allocation.
[akpm@linux-foundation.org: remove unneeded __must_check]
[david-b@pacbell.net: don't export gpiochip_reserve (section fix)]
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 2ba6127..24c62b8 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -43,6 +43,7 @@ struct gpio_desc {
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
+#define FLAG_RESERVED 2
#ifdef CONFIG_DEBUG_FS
const char *label;
@@ -88,9 +89,10 @@ static int gpiochip_find_base(int ngpio)
int base = -ENOSPC;
for (i = ARCH_NR_GPIOS - 1; i >= 0 ; i--) {
- struct gpio_chip *chip = gpio_desc[i].chip;
+ struct gpio_desc *desc = &gpio_desc[i];
+ struct gpio_chip *chip = desc->chip;
- if (!chip) {
+ if (!chip && !test_bit(FLAG_RESERVED, &desc->flags)) {
spare++;
if (spare == ngpio) {
base = i;
@@ -98,7 +100,8 @@ static int gpiochip_find_base(int ngpio)
}
} else {
spare = 0;
- i -= chip->ngpio - 1;
+ if (chip)
+ i -= chip->ngpio - 1;
}
}
@@ -108,6 +111,47 @@ static int gpiochip_find_base(int ngpio)
}
/**
+ * gpiochip_reserve() - reserve range of gpios to use with platform code only
+ * @start: starting gpio number
+ * @ngpio: number of gpios to reserve
+ * Context: platform init, potentially before irqs or kmalloc will work
+ *
+ * Returns a negative errno if any gpio within the range is already reserved
+ * or registered, else returns zero as a success code. Use this function
+ * to mark a range of gpios as unavailable for dynamic gpio number allocation,
+ * for example because its driver support is not yet loaded.
+ */
+int __init gpiochip_reserve(int start, int ngpio)
+{
+ int ret = 0;
+ unsigned long flags;
+ int i;
+
+ if (!gpio_is_valid(start) || !gpio_is_valid(start + ngpio))
+ return -EINVAL;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ for (i = start; i < start + ngpio; i++) {
+ struct gpio_desc *desc = &gpio_desc[i];
+
+ if (desc->chip || test_bit(FLAG_RESERVED, &desc->flags)) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ set_bit(FLAG_RESERVED, &desc->flags);
+ }
+
+ pr_debug("%s: reserved gpios from %d to %d\n",
+ __func__, start, start + ngpio - 1);
+err:
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return ret;
+}
+
+/**
* gpiochip_add() - register a gpio_chip
* @chip: the chip to register, with chip->base initialized
* Context: potentially before irqs or kmalloc will work
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 464c5b3..ecf675a 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -74,6 +74,7 @@ struct gpio_chip {
extern const char *gpiochip_is_requested(struct gpio_chip *chip,
unsigned offset);
+extern int __init __must_check gpiochip_reserve(int start, int ngpio);
/* add/remove chips */
extern int gpiochip_add(struct gpio_chip *chip);

View file

@ -0,0 +1,48 @@
From: Trent Piepho <xyzzy@speakeasy.org>
Date: Fri, 23 May 2008 20:04:44 +0000 (-0700)
Subject: gpiolib: fix off by one errors
X-Git-Tag: v2.6.26-rc4~31
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=bff5fda972dc23bd1806a47c2098ae173585d013
gpiolib: fix off by one errors
The last gpio belonging to a chip is chip->base + chip->ngpios - 1. Some
places in the code, but not all, forgot the critical minus one.
Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 7f138c6..beaf6b3 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -127,7 +127,7 @@ int __init gpiochip_reserve(int start, int ngpio)
unsigned long flags;
int i;
- if (!gpio_is_valid(start) || !gpio_is_valid(start + ngpio))
+ if (!gpio_is_valid(start) || !gpio_is_valid(start + ngpio - 1))
return -EINVAL;
spin_lock_irqsave(&gpio_lock, flags);
@@ -170,7 +170,7 @@ int gpiochip_add(struct gpio_chip *chip)
unsigned id;
int base = chip->base;
- if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio))
+ if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
&& base >= 0) {
status = -EINVAL;
goto fail;
@@ -207,7 +207,7 @@ fail:
/* failures here can mean systems won't boot... */
if (status)
pr_err("gpiochip_add: gpios %d..%d (%s) not registered\n",
- chip->base, chip->base + chip->ngpio,
+ chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return status;
}

View file

@ -83,6 +83,7 @@ config TARGET_OPTIMIZATION
default "-O2 -pipe -march=i686 -funit-at-a-time" if TARGET_x86_mediacenter
default "-O2 -pipe -march=i486 -funit-at-a-time" if TARGET_x86
default "-Os -pipe -march=i486 -funit-at-a-time" if TARGET_rdc
default "-Os -pipe -mips32r2 -mtune=mips32r2 -funit-at-a-time" if TARGET_ar71xx
default "-Os -pipe -mips32 -mtune=mips32 -funit-at-a-time" if mipsel || mips
default "-Os -pipe -march=armv5te -mtune=xscale -funit-at-a-time" if TARGET_ixp4xx || TARGET_iop32x || TARGET_pxa || TARGET_orion
default "-Os -pipe -march=armv4t -mtune=arm9tdmi -funit-at-a-time" if TARGET_storm