diff options
Diffstat (limited to 'arch/mips/alchemy/common')
| -rw-r--r-- | arch/mips/alchemy/common/Makefile | 12 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/clocks.c | 13 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/dbdma.c | 514 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/dma.c | 98 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/gpio.c | 148 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/gpiolib.c | 175 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/irq.c | 1292 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/pci.c | 104 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/platform.c | 650 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/power.c | 115 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/prom.c | 33 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/puts.c | 68 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/reset.c | 187 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/setup.c | 62 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/sleeper.S | 170 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/time.c | 58 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/usb.c | 593 | ||||
| -rw-r--r-- | arch/mips/alchemy/common/vss.c | 84 |
18 files changed, 2623 insertions, 1753 deletions
diff --git a/arch/mips/alchemy/common/Makefile b/arch/mips/alchemy/common/Makefile index d50d4764eaf..cb83d8d21ae 100644 --- a/arch/mips/alchemy/common/Makefile +++ b/arch/mips/alchemy/common/Makefile @@ -5,10 +5,10 @@ # Makefile for the Alchemy Au1xx0 CPUs, generic files. # -obj-y += prom.o irq.o puts.o time.o reset.o \ - clocks.o platform.o power.o setup.o \ - sleeper.o dma.o dbdma.o gpio.o +obj-y += prom.o time.o clocks.o platform.o power.o setup.o \ + sleeper.o dma.o dbdma.o vss.o irq.o usb.o -obj-$(CONFIG_PCI) += pci.o - -EXTRA_CFLAGS += -Werror +# optional gpiolib support +ifeq ($(CONFIG_ALCHEMY_GPIO_INDIRECT),) + obj-$(CONFIG_GPIOLIB) += gpiolib.o +endif diff --git a/arch/mips/alchemy/common/clocks.c b/arch/mips/alchemy/common/clocks.c index d8991854530..f38298a8b98 100644 --- a/arch/mips/alchemy/common/clocks.c +++ b/arch/mips/alchemy/common/clocks.c @@ -40,8 +40,6 @@ static unsigned int au1x00_clock; /* Hz */ static unsigned long uart_baud_base; -static DEFINE_SPINLOCK(time_lock); - /* * Set the au1000_clock */ @@ -77,16 +75,13 @@ void set_au1x00_uart_baud_base(unsigned long new_baud_base) * counter, if it exists. If we don't have an accurate processor * speed, all of the peripherals that derive their clocks based on * this advertised speed will introduce error and sometimes not work - * properly. This function is futher convoluted to still allow configurations + * properly. This function is further convoluted to still allow configurations * to do that in case they have really, really old silicon with a * write-only PLL register. -- Dan */ unsigned long au1xxx_calc_clock(void) { unsigned long cpu_speed; - unsigned long flags; - - spin_lock_irqsave(&time_lock, flags); /* * On early Au1000, sys_cpupll was write-only. Since these @@ -94,11 +89,7 @@ unsigned long au1xxx_calc_clock(void) * over backwards trying to determine the frequency. */ if (au1xxx_cpu_has_pll_wo()) -#ifdef CONFIG_SOC_AU1000_FREQUENCY - cpu_speed = CONFIG_SOC_AU1000_FREQUENCY; -#else cpu_speed = 396000000; -#endif else cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK; @@ -108,8 +99,6 @@ unsigned long au1xxx_calc_clock(void) set_au1x00_uart_baud_base(cpu_speed / (2 * ((int)(au_readl(SYS_POWERCTRL) & 0x03) + 2) * 16)); - spin_unlock_irqrestore(&time_lock, flags); - set_au1x00_speed(cpu_speed); return cpu_speed; diff --git a/arch/mips/alchemy/common/dbdma.c b/arch/mips/alchemy/common/dbdma.c index 3ab6d80d150..19d5642c16d 100644 --- a/arch/mips/alchemy/common/dbdma.c +++ b/arch/mips/alchemy/common/dbdma.c @@ -30,16 +30,16 @@ * */ +#include <linux/init.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/syscore_ops.h> #include <asm/mach-au1x00/au1000.h> #include <asm/mach-au1x00/au1xxx_dbdma.h> -#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) - /* * The Descriptor Based DMA supports up to 16 channels. * @@ -56,128 +56,144 @@ static DEFINE_SPINLOCK(au1xxx_dbdma_spin_lock); /* I couldn't find a macro that did this... */ #define ALIGN_ADDR(x, a) ((((u32)(x)) + (a-1)) & ~(a-1)) -static dbdma_global_t *dbdma_gptr = (dbdma_global_t *)DDMA_GLOBAL_BASE; +static dbdma_global_t *dbdma_gptr = + (dbdma_global_t *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); static int dbdma_initialized; -static void au1xxx_dbdma_init(void); -static dbdev_tab_t dbdev_tab[] = { -#ifdef CONFIG_SOC_AU1550 +static dbdev_tab_t *dbdev_tab; + +static dbdev_tab_t au1550_dbdev_tab[] __initdata = { /* UARTS */ - { DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 }, - { DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 }, - { DSCR_CMD0_UART3_TX, DEV_FLAGS_OUT, 0, 8, 0x11400004, 0, 0 }, - { DSCR_CMD0_UART3_RX, DEV_FLAGS_IN, 0, 8, 0x11400000, 0, 0 }, + { AU1550_DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 }, + { AU1550_DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 }, + { AU1550_DSCR_CMD0_UART3_TX, DEV_FLAGS_OUT, 0, 8, 0x11400004, 0, 0 }, + { AU1550_DSCR_CMD0_UART3_RX, DEV_FLAGS_IN, 0, 8, 0x11400000, 0, 0 }, /* EXT DMA */ - { DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_DMA_REQ2, 0, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_DMA_REQ3, 0, 0, 0, 0x00000000, 0, 0 }, + { AU1550_DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, + { AU1550_DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, + { AU1550_DSCR_CMD0_DMA_REQ2, 0, 0, 0, 0x00000000, 0, 0 }, + { AU1550_DSCR_CMD0_DMA_REQ3, 0, 0, 0, 0x00000000, 0, 0 }, /* USB DEV */ - { DSCR_CMD0_USBDEV_RX0, DEV_FLAGS_IN, 4, 8, 0x10200000, 0, 0 }, - { DSCR_CMD0_USBDEV_TX0, DEV_FLAGS_OUT, 4, 8, 0x10200004, 0, 0 }, - { DSCR_CMD0_USBDEV_TX1, DEV_FLAGS_OUT, 4, 8, 0x10200008, 0, 0 }, - { DSCR_CMD0_USBDEV_TX2, DEV_FLAGS_OUT, 4, 8, 0x1020000c, 0, 0 }, - { DSCR_CMD0_USBDEV_RX3, DEV_FLAGS_IN, 4, 8, 0x10200010, 0, 0 }, - { DSCR_CMD0_USBDEV_RX4, DEV_FLAGS_IN, 4, 8, 0x10200014, 0, 0 }, + { AU1550_DSCR_CMD0_USBDEV_RX0, DEV_FLAGS_IN, 4, 8, 0x10200000, 0, 0 }, + { AU1550_DSCR_CMD0_USBDEV_TX0, DEV_FLAGS_OUT, 4, 8, 0x10200004, 0, 0 }, + { AU1550_DSCR_CMD0_USBDEV_TX1, DEV_FLAGS_OUT, 4, 8, 0x10200008, 0, 0 }, + { AU1550_DSCR_CMD0_USBDEV_TX2, DEV_FLAGS_OUT, 4, 8, 0x1020000c, 0, 0 }, + { AU1550_DSCR_CMD0_USBDEV_RX3, DEV_FLAGS_IN, 4, 8, 0x10200010, 0, 0 }, + { AU1550_DSCR_CMD0_USBDEV_RX4, DEV_FLAGS_IN, 4, 8, 0x10200014, 0, 0 }, + + /* PSCs */ + { AU1550_DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 0, 0x11a0001c, 0, 0 }, + { AU1550_DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 0, 0x11a0001c, 0, 0 }, + { AU1550_DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 0, 0x11b0001c, 0, 0 }, + { AU1550_DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 0, 0x11b0001c, 0, 0 }, + { AU1550_DSCR_CMD0_PSC2_TX, DEV_FLAGS_OUT, 0, 0, 0x10a0001c, 0, 0 }, + { AU1550_DSCR_CMD0_PSC2_RX, DEV_FLAGS_IN, 0, 0, 0x10a0001c, 0, 0 }, + { AU1550_DSCR_CMD0_PSC3_TX, DEV_FLAGS_OUT, 0, 0, 0x10b0001c, 0, 0 }, + { AU1550_DSCR_CMD0_PSC3_RX, DEV_FLAGS_IN, 0, 0, 0x10b0001c, 0, 0 }, + + { AU1550_DSCR_CMD0_PCI_WRITE, 0, 0, 0, 0x00000000, 0, 0 }, /* PCI */ + { AU1550_DSCR_CMD0_NAND_FLASH, 0, 0, 0, 0x00000000, 0, 0 }, /* NAND */ - /* PSC 0 */ - { DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 0, 0x11a0001c, 0, 0 }, - { DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 0, 0x11a0001c, 0, 0 }, + /* MAC 0 */ + { AU1550_DSCR_CMD0_MAC0_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, + { AU1550_DSCR_CMD0_MAC0_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, - /* PSC 1 */ - { DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 0, 0x11b0001c, 0, 0 }, - { DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 0, 0x11b0001c, 0, 0 }, + /* MAC 1 */ + { AU1550_DSCR_CMD0_MAC1_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, + { AU1550_DSCR_CMD0_MAC1_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, - /* PSC 2 */ - { DSCR_CMD0_PSC2_TX, DEV_FLAGS_OUT, 0, 0, 0x10a0001c, 0, 0 }, - { DSCR_CMD0_PSC2_RX, DEV_FLAGS_IN, 0, 0, 0x10a0001c, 0, 0 }, + { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, +}; - /* PSC 3 */ - { DSCR_CMD0_PSC3_TX, DEV_FLAGS_OUT, 0, 0, 0x10b0001c, 0, 0 }, - { DSCR_CMD0_PSC3_RX, DEV_FLAGS_IN, 0, 0, 0x10b0001c, 0, 0 }, +static dbdev_tab_t au1200_dbdev_tab[] __initdata = { + { AU1200_DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 }, + { AU1200_DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 }, + { AU1200_DSCR_CMD0_UART1_TX, DEV_FLAGS_OUT, 0, 8, 0x11200004, 0, 0 }, + { AU1200_DSCR_CMD0_UART1_RX, DEV_FLAGS_IN, 0, 8, 0x11200000, 0, 0 }, - { DSCR_CMD0_PCI_WRITE, 0, 0, 0, 0x00000000, 0, 0 }, /* PCI */ - { DSCR_CMD0_NAND_FLASH, 0, 0, 0, 0x00000000, 0, 0 }, /* NAND */ + { AU1200_DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, + { AU1200_DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, - /* MAC 0 */ - { DSCR_CMD0_MAC0_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_MAC0_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, + { AU1200_DSCR_CMD0_MAE_BE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { AU1200_DSCR_CMD0_MAE_FE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { AU1200_DSCR_CMD0_MAE_BOTH, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { AU1200_DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, - /* MAC 1 */ - { DSCR_CMD0_MAC1_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_MAC1_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, + { AU1200_DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 4, 8, 0x10600000, 0, 0 }, + { AU1200_DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 4, 8, 0x10600004, 0, 0 }, + { AU1200_DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 4, 8, 0x10680000, 0, 0 }, + { AU1200_DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 4, 8, 0x10680004, 0, 0 }, -#endif /* CONFIG_SOC_AU1550 */ + { AU1200_DSCR_CMD0_AES_RX, DEV_FLAGS_IN , 4, 32, 0x10300008, 0, 0 }, + { AU1200_DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 4, 32, 0x10300004, 0, 0 }, -#ifdef CONFIG_SOC_AU1200 - { DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 }, - { DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 }, - { DSCR_CMD0_UART1_TX, DEV_FLAGS_OUT, 0, 8, 0x11200004, 0, 0 }, - { DSCR_CMD0_UART1_RX, DEV_FLAGS_IN, 0, 8, 0x11200000, 0, 0 }, + { AU1200_DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 16, 0x11a0001c, 0, 0 }, + { AU1200_DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 16, 0x11a0001c, 0, 0 }, + { AU1200_DSCR_CMD0_PSC0_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { AU1200_DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 16, 0x11b0001c, 0, 0 }, + { AU1200_DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 16, 0x11b0001c, 0, 0 }, + { AU1200_DSCR_CMD0_PSC1_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, + { AU1200_DSCR_CMD0_CIM_RXA, DEV_FLAGS_IN, 0, 32, 0x14004020, 0, 0 }, + { AU1200_DSCR_CMD0_CIM_RXB, DEV_FLAGS_IN, 0, 32, 0x14004040, 0, 0 }, + { AU1200_DSCR_CMD0_CIM_RXC, DEV_FLAGS_IN, 0, 32, 0x14004060, 0, 0 }, + { AU1200_DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_MAE_BE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_MAE_FE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_MAE_BOTH, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { AU1200_DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 4, 8, 0x10600000, 0, 0 }, - { DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 4, 8, 0x10600004, 0, 0 }, - { DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 4, 8, 0x10680000, 0, 0 }, - { DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 4, 8, 0x10680004, 0, 0 }, + { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, +}; - { DSCR_CMD0_AES_RX, DEV_FLAGS_IN , 4, 32, 0x10300008, 0, 0 }, - { DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 4, 32, 0x10300004, 0, 0 }, +static dbdev_tab_t au1300_dbdev_tab[] __initdata = { + { AU1300_DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x10100004, 0, 0 }, + { AU1300_DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x10100000, 0, 0 }, + { AU1300_DSCR_CMD0_UART1_TX, DEV_FLAGS_OUT, 0, 8, 0x10101004, 0, 0 }, + { AU1300_DSCR_CMD0_UART1_RX, DEV_FLAGS_IN, 0, 8, 0x10101000, 0, 0 }, + { AU1300_DSCR_CMD0_UART2_TX, DEV_FLAGS_OUT, 0, 8, 0x10102004, 0, 0 }, + { AU1300_DSCR_CMD0_UART2_RX, DEV_FLAGS_IN, 0, 8, 0x10102000, 0, 0 }, + { AU1300_DSCR_CMD0_UART3_TX, DEV_FLAGS_OUT, 0, 8, 0x10103004, 0, 0 }, + { AU1300_DSCR_CMD0_UART3_RX, DEV_FLAGS_IN, 0, 8, 0x10103000, 0, 0 }, - { DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 16, 0x11a0001c, 0, 0 }, - { DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 16, 0x11a0001c, 0, 0 }, - { DSCR_CMD0_PSC0_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { AU1300_DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 4, 8, 0x10600000, 0, 0 }, + { AU1300_DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 4, 8, 0x10600004, 0, 0 }, + { AU1300_DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 8, 8, 0x10601000, 0, 0 }, + { AU1300_DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 8, 8, 0x10601004, 0, 0 }, - { DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 16, 0x11b0001c, 0, 0 }, - { DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 16, 0x11b0001c, 0, 0 }, - { DSCR_CMD0_PSC1_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { AU1300_DSCR_CMD0_AES_RX, DEV_FLAGS_IN , 4, 32, 0x10300008, 0, 0 }, + { AU1300_DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 4, 32, 0x10300004, 0, 0 }, - { DSCR_CMD0_CIM_RXA, DEV_FLAGS_IN, 0, 32, 0x14004020, 0, 0 }, - { DSCR_CMD0_CIM_RXB, DEV_FLAGS_IN, 0, 32, 0x14004040, 0, 0 }, - { DSCR_CMD0_CIM_RXC, DEV_FLAGS_IN, 0, 32, 0x14004060, 0, 0 }, - { DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { AU1300_DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0001c, 0, 0 }, + { AU1300_DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 16, 0x10a0001c, 0, 0 }, + { AU1300_DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0101c, 0, 0 }, + { AU1300_DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 16, 0x10a0101c, 0, 0 }, + { AU1300_DSCR_CMD0_PSC2_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0201c, 0, 0 }, + { AU1300_DSCR_CMD0_PSC2_RX, DEV_FLAGS_IN, 0, 16, 0x10a0201c, 0, 0 }, + { AU1300_DSCR_CMD0_PSC3_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0301c, 0, 0 }, + { AU1300_DSCR_CMD0_PSC3_RX, DEV_FLAGS_IN, 0, 16, 0x10a0301c, 0, 0 }, - { DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, + { AU1300_DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { AU1300_DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, -#endif /* CONFIG_SOC_AU1200 */ + { AU1300_DSCR_CMD0_SDMS_TX2, DEV_FLAGS_OUT, 4, 8, 0x10602000, 0, 0 }, + { AU1300_DSCR_CMD0_SDMS_RX2, DEV_FLAGS_IN, 4, 8, 0x10602004, 0, 0 }, - { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, - { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, - - /* Provide 16 user definable device types */ - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, - { ~0, 0, 0, 0, 0, 0, 0 }, -}; + { AU1300_DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, -#define DBDEV_TAB_SIZE ARRAY_SIZE(dbdev_tab) + { AU1300_DSCR_CMD0_UDMA, DEV_FLAGS_ANYUSE, 0, 32, 0x14001810, 0, 0 }, -#ifdef CONFIG_PM -static u32 au1xxx_dbdma_pm_regs[NUM_DBDMA_CHANS + 1][8]; -#endif + { AU1300_DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, + { AU1300_DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, + { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, + { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, +}; + +/* 32 predefined plus 32 custom */ +#define DBDEV_TAB_SIZE 64 static chan_tab_t *chan_tab_ptr[NUM_DBDMA_CHANS]; @@ -236,8 +252,8 @@ EXPORT_SYMBOL(au1xxx_ddma_del_device); u32 au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid, void (*callback)(int, void *), void *callparam) { - unsigned long flags; - u32 used, chan, rv; + unsigned long flags; + u32 used, chan; u32 dcp; int i; dbdev_tab_t *stp, *dtp; @@ -250,8 +266,7 @@ u32 au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid, * which can't be done successfully during board set up. */ if (!dbdma_initialized) - au1xxx_dbdma_init(); - dbdma_initialized = 1; + return 0; stp = find_dbdev_id(srcid); if (stp == NULL) @@ -261,7 +276,6 @@ u32 au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid, return 0; used = 0; - rv = 0; /* Check to see if we can get both channels. */ spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags); @@ -282,63 +296,65 @@ u32 au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid, used++; spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags); - if (!used) { - /* Let's see if we can allocate a channel for it. */ - ctp = NULL; - chan = 0; - spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags); - for (i = 0; i < NUM_DBDMA_CHANS; i++) - if (chan_tab_ptr[i] == NULL) { - /* - * If kmalloc fails, it is caught below same - * as a channel not available. - */ - ctp = kmalloc(sizeof(chan_tab_t), GFP_ATOMIC); - chan_tab_ptr[i] = ctp; - break; - } - spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags); - - if (ctp != NULL) { - memset(ctp, 0, sizeof(chan_tab_t)); - ctp->chan_index = chan = i; - dcp = DDMA_CHANNEL_BASE; - dcp += (0x0100 * chan); - ctp->chan_ptr = (au1x_dma_chan_t *)dcp; - cp = (au1x_dma_chan_t *)dcp; - ctp->chan_src = stp; - ctp->chan_dest = dtp; - ctp->chan_callback = callback; - ctp->chan_callparam = callparam; - - /* Initialize channel configuration. */ - i = 0; - if (stp->dev_intlevel) - i |= DDMA_CFG_SED; - if (stp->dev_intpolarity) - i |= DDMA_CFG_SP; - if (dtp->dev_intlevel) - i |= DDMA_CFG_DED; - if (dtp->dev_intpolarity) - i |= DDMA_CFG_DP; - if ((stp->dev_flags & DEV_FLAGS_SYNC) || - (dtp->dev_flags & DEV_FLAGS_SYNC)) - i |= DDMA_CFG_SYNC; - cp->ddma_cfg = i; - au_sync(); - - /* Return a non-zero value that can be used to - * find the channel information in subsequent - * operations. + if (used) + return 0; + + /* Let's see if we can allocate a channel for it. */ + ctp = NULL; + chan = 0; + spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags); + for (i = 0; i < NUM_DBDMA_CHANS; i++) + if (chan_tab_ptr[i] == NULL) { + /* + * If kmalloc fails, it is caught below same + * as a channel not available. */ - rv = (u32)(&chan_tab_ptr[chan]); - } else { - /* Release devices */ - stp->dev_flags &= ~DEV_FLAGS_INUSE; - dtp->dev_flags &= ~DEV_FLAGS_INUSE; + ctp = kmalloc(sizeof(chan_tab_t), GFP_ATOMIC); + chan_tab_ptr[i] = ctp; + break; } + spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags); + + if (ctp != NULL) { + memset(ctp, 0, sizeof(chan_tab_t)); + ctp->chan_index = chan = i; + dcp = KSEG1ADDR(AU1550_DBDMA_PHYS_ADDR); + dcp += (0x0100 * chan); + ctp->chan_ptr = (au1x_dma_chan_t *)dcp; + cp = (au1x_dma_chan_t *)dcp; + ctp->chan_src = stp; + ctp->chan_dest = dtp; + ctp->chan_callback = callback; + ctp->chan_callparam = callparam; + + /* Initialize channel configuration. */ + i = 0; + if (stp->dev_intlevel) + i |= DDMA_CFG_SED; + if (stp->dev_intpolarity) + i |= DDMA_CFG_SP; + if (dtp->dev_intlevel) + i |= DDMA_CFG_DED; + if (dtp->dev_intpolarity) + i |= DDMA_CFG_DP; + if ((stp->dev_flags & DEV_FLAGS_SYNC) || + (dtp->dev_flags & DEV_FLAGS_SYNC)) + i |= DDMA_CFG_SYNC; + cp->ddma_cfg = i; + au_sync(); + + /* + * Return a non-zero value that can be used to find the channel + * information in subsequent operations. + */ + return (u32)(&chan_tab_ptr[chan]); } - return rv; + + /* Release devices */ + stp->dev_flags &= ~DEV_FLAGS_INUSE; + dtp->dev_flags &= ~DEV_FLAGS_INUSE; + + return 0; } EXPORT_SYMBOL(au1xxx_dbdma_chan_alloc); @@ -412,8 +428,11 @@ u32 au1xxx_dbdma_ring_alloc(u32 chanid, int entries) if (desc_base == 0) return 0; + ctp->cdb_membase = desc_base; desc_base = ALIGN_ADDR(desc_base, sizeof(au1x_ddma_desc_t)); - } + } else + ctp->cdb_membase = desc_base; + dp = (au1x_ddma_desc_t *)desc_base; /* Keep track of the base descriptor. */ @@ -493,7 +512,7 @@ u32 au1xxx_dbdma_ring_alloc(u32 chanid, int entries) break; } - /* If source input is FIFO, set static address. */ + /* If source input is FIFO, set static address. */ if (stp->dev_flags & DEV_FLAGS_IN) { if (stp->dev_flags & DEV_FLAGS_BURSTABLE) src1 |= DSCR_SRC1_SAM(DSCR_xAM_BURST); @@ -569,7 +588,7 @@ EXPORT_SYMBOL(au1xxx_dbdma_ring_alloc); * This updates the source pointer and byte count. Normally used * for memory to fifo transfers. */ -u32 _au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes, u32 flags) +u32 au1xxx_dbdma_put_source(u32 chanid, dma_addr_t buf, int nbytes, u32 flags) { chan_tab_t *ctp; au1x_ddma_desc_t *dp; @@ -595,7 +614,7 @@ u32 _au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes, u32 flags) return 0; /* Load up buffer address and byte count. */ - dp->dscr_source0 = virt_to_phys(buf); + dp->dscr_source0 = buf & ~0UL; dp->dscr_cmd1 = nbytes; /* Check flags */ if (flags & DDMA_FLAGS_IE) @@ -613,23 +632,22 @@ u32 _au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes, u32 flags) dma_cache_wback_inv((unsigned long)buf, nbytes); dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */ au_sync(); - dma_cache_wback_inv((unsigned long)dp, sizeof(dp)); + dma_cache_wback_inv((unsigned long)dp, sizeof(*dp)); ctp->chan_ptr->ddma_dbell = 0; - /* Get next descriptor pointer. */ + /* Get next descriptor pointer. */ ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); /* Return something non-zero. */ return nbytes; } -EXPORT_SYMBOL(_au1xxx_dbdma_put_source); +EXPORT_SYMBOL(au1xxx_dbdma_put_source); /* Put a destination buffer into the DMA ring. * This updates the destination pointer and byte count. Normally used * to place an empty buffer into the ring for fifo to memory transfers. */ -u32 -_au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes, u32 flags) +u32 au1xxx_dbdma_put_dest(u32 chanid, dma_addr_t buf, int nbytes, u32 flags) { chan_tab_t *ctp; au1x_ddma_desc_t *dp; @@ -659,7 +677,7 @@ _au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes, u32 flags) if (flags & DDMA_FLAGS_NOIE) dp->dscr_cmd0 &= ~DSCR_CMD0_IE; - dp->dscr_dest0 = virt_to_phys(buf); + dp->dscr_dest0 = buf & ~0UL; dp->dscr_cmd1 = nbytes; #if 0 printk(KERN_DEBUG "cmd0:%x cmd1:%x source0:%x source1:%x dest0:%x dest1:%x\n", @@ -676,16 +694,16 @@ _au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes, u32 flags) dma_cache_inv((unsigned long)buf, nbytes); dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */ au_sync(); - dma_cache_wback_inv((unsigned long)dp, sizeof(dp)); + dma_cache_wback_inv((unsigned long)dp, sizeof(*dp)); ctp->chan_ptr->ddma_dbell = 0; - /* Get next descriptor pointer. */ + /* Get next descriptor pointer. */ ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); /* Return something non-zero. */ return nbytes; } -EXPORT_SYMBOL(_au1xxx_dbdma_put_dest); +EXPORT_SYMBOL(au1xxx_dbdma_put_dest); /* * Get a destination buffer into the DMA ring. @@ -724,7 +742,7 @@ u32 au1xxx_dbdma_get_dest(u32 chanid, void **buf, int *nbytes) *nbytes = dp->dscr_cmd1; rv = dp->dscr_stat; - /* Get next descriptor pointer. */ + /* Get next descriptor pointer. */ ctp->get_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); /* Return something non-zero. */ @@ -831,7 +849,7 @@ void au1xxx_dbdma_chan_free(u32 chanid) au1xxx_dbdma_stop(chanid); - kfree((void *)ctp->chan_desc_base); + kfree((void *)ctp->cdb_membase); stp->dev_flags &= ~DEV_FLAGS_INUSE; dtp->dev_flags &= ~DEV_FLAGS_INUSE; @@ -868,34 +886,12 @@ static irqreturn_t dbdma_interrupt(int irq, void *dev_id) return IRQ_RETVAL(1); } -static void au1xxx_dbdma_init(void) -{ - int irq_nr; - - dbdma_gptr->ddma_config = 0; - dbdma_gptr->ddma_throttle = 0; - dbdma_gptr->ddma_inten = 0xffff; - au_sync(); - -#if defined(CONFIG_SOC_AU1550) - irq_nr = AU1550_DDMA_INT; -#elif defined(CONFIG_SOC_AU1200) - irq_nr = AU1200_DDMA_INT; -#else - #error Unknown Au1x00 SOC -#endif - - if (request_irq(irq_nr, dbdma_interrupt, IRQF_DISABLED, - "Au1xxx dbdma", (void *)dbdma_gptr)) - printk(KERN_ERR "Can't get 1550 dbdma irq"); -} - void au1xxx_dbdma_dump(u32 chanid) { chan_tab_t *ctp; au1x_ddma_desc_t *dp; dbdev_tab_t *stp, *dtp; - au1x_dma_chan_t *cp; + au1x_dma_chan_t *cp; u32 i = 0; ctp = *((chan_tab_t **)chanid); @@ -903,7 +899,7 @@ void au1xxx_dbdma_dump(u32 chanid) dtp = ctp->chan_dest; cp = ctp->chan_ptr; - printk(KERN_DEBUG "Chan %x, stp %x (dev %d) dtp %x (dev %d) \n", + printk(KERN_DEBUG "Chan %x, stp %x (dev %d) dtp %x (dev %d)\n", (u32)ctp, (u32)stp, stp - dbdev_tab, (u32)dtp, dtp - dbdev_tab); printk(KERN_DEBUG "desc base %x, get %x, put %x, cur %x\n", @@ -973,71 +969,121 @@ u32 au1xxx_dbdma_put_dscr(u32 chanid, au1x_ddma_desc_t *dscr) dp->dscr_cmd0 |= dscr->dscr_cmd0 | DSCR_CMD0_V; ctp->chan_ptr->ddma_dbell = 0; - /* Get next descriptor pointer. */ + /* Get next descriptor pointer. */ ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); /* Return something non-zero. */ return nbytes; } -#ifdef CONFIG_PM -void au1xxx_dbdma_suspend(void) + +static unsigned long alchemy_dbdma_pm_data[NUM_DBDMA_CHANS + 1][6]; + +static int alchemy_dbdma_suspend(void) { int i; - u32 addr; + void __iomem *addr; - addr = DDMA_GLOBAL_BASE; - au1xxx_dbdma_pm_regs[0][0] = au_readl(addr + 0x00); - au1xxx_dbdma_pm_regs[0][1] = au_readl(addr + 0x04); - au1xxx_dbdma_pm_regs[0][2] = au_readl(addr + 0x08); - au1xxx_dbdma_pm_regs[0][3] = au_readl(addr + 0x0c); + addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); + alchemy_dbdma_pm_data[0][0] = __raw_readl(addr + 0x00); + alchemy_dbdma_pm_data[0][1] = __raw_readl(addr + 0x04); + alchemy_dbdma_pm_data[0][2] = __raw_readl(addr + 0x08); + alchemy_dbdma_pm_data[0][3] = __raw_readl(addr + 0x0c); /* save channel configurations */ - for (i = 1, addr = DDMA_CHANNEL_BASE; i < NUM_DBDMA_CHANS; i++) { - au1xxx_dbdma_pm_regs[i][0] = au_readl(addr + 0x00); - au1xxx_dbdma_pm_regs[i][1] = au_readl(addr + 0x04); - au1xxx_dbdma_pm_regs[i][2] = au_readl(addr + 0x08); - au1xxx_dbdma_pm_regs[i][3] = au_readl(addr + 0x0c); - au1xxx_dbdma_pm_regs[i][4] = au_readl(addr + 0x10); - au1xxx_dbdma_pm_regs[i][5] = au_readl(addr + 0x14); - au1xxx_dbdma_pm_regs[i][6] = au_readl(addr + 0x18); + addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_PHYS_ADDR); + for (i = 1; i <= NUM_DBDMA_CHANS; i++) { + alchemy_dbdma_pm_data[i][0] = __raw_readl(addr + 0x00); + alchemy_dbdma_pm_data[i][1] = __raw_readl(addr + 0x04); + alchemy_dbdma_pm_data[i][2] = __raw_readl(addr + 0x08); + alchemy_dbdma_pm_data[i][3] = __raw_readl(addr + 0x0c); + alchemy_dbdma_pm_data[i][4] = __raw_readl(addr + 0x10); + alchemy_dbdma_pm_data[i][5] = __raw_readl(addr + 0x14); /* halt channel */ - au_writel(au1xxx_dbdma_pm_regs[i][0] & ~1, addr + 0x00); - au_sync(); - while (!(au_readl(addr + 0x14) & 1)) - au_sync(); + __raw_writel(alchemy_dbdma_pm_data[i][0] & ~1, addr + 0x00); + wmb(); + while (!(__raw_readl(addr + 0x14) & 1)) + wmb(); addr += 0x100; /* next channel base */ } /* disable channel interrupts */ - au_writel(0, DDMA_GLOBAL_BASE + 0x0c); - au_sync(); + addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); + __raw_writel(0, addr + 0x0c); + wmb(); + + return 0; } -void au1xxx_dbdma_resume(void) +static void alchemy_dbdma_resume(void) { int i; - u32 addr; + void __iomem *addr; - addr = DDMA_GLOBAL_BASE; - au_writel(au1xxx_dbdma_pm_regs[0][0], addr + 0x00); - au_writel(au1xxx_dbdma_pm_regs[0][1], addr + 0x04); - au_writel(au1xxx_dbdma_pm_regs[0][2], addr + 0x08); - au_writel(au1xxx_dbdma_pm_regs[0][3], addr + 0x0c); + addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); + __raw_writel(alchemy_dbdma_pm_data[0][0], addr + 0x00); + __raw_writel(alchemy_dbdma_pm_data[0][1], addr + 0x04); + __raw_writel(alchemy_dbdma_pm_data[0][2], addr + 0x08); + __raw_writel(alchemy_dbdma_pm_data[0][3], addr + 0x0c); /* restore channel configurations */ - for (i = 1, addr = DDMA_CHANNEL_BASE; i < NUM_DBDMA_CHANS; i++) { - au_writel(au1xxx_dbdma_pm_regs[i][0], addr + 0x00); - au_writel(au1xxx_dbdma_pm_regs[i][1], addr + 0x04); - au_writel(au1xxx_dbdma_pm_regs[i][2], addr + 0x08); - au_writel(au1xxx_dbdma_pm_regs[i][3], addr + 0x0c); - au_writel(au1xxx_dbdma_pm_regs[i][4], addr + 0x10); - au_writel(au1xxx_dbdma_pm_regs[i][5], addr + 0x14); - au_writel(au1xxx_dbdma_pm_regs[i][6], addr + 0x18); - au_sync(); + addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_PHYS_ADDR); + for (i = 1; i <= NUM_DBDMA_CHANS; i++) { + __raw_writel(alchemy_dbdma_pm_data[i][0], addr + 0x00); + __raw_writel(alchemy_dbdma_pm_data[i][1], addr + 0x04); + __raw_writel(alchemy_dbdma_pm_data[i][2], addr + 0x08); + __raw_writel(alchemy_dbdma_pm_data[i][3], addr + 0x0c); + __raw_writel(alchemy_dbdma_pm_data[i][4], addr + 0x10); + __raw_writel(alchemy_dbdma_pm_data[i][5], addr + 0x14); + wmb(); addr += 0x100; /* next channel base */ } } -#endif /* CONFIG_PM */ -#endif /* defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) */ + +static struct syscore_ops alchemy_dbdma_syscore_ops = { + .suspend = alchemy_dbdma_suspend, + .resume = alchemy_dbdma_resume, +}; + +static int __init dbdma_setup(unsigned int irq, dbdev_tab_t *idtable) +{ + int ret; + + dbdev_tab = kzalloc(sizeof(dbdev_tab_t) * DBDEV_TAB_SIZE, GFP_KERNEL); + if (!dbdev_tab) + return -ENOMEM; + + memcpy(dbdev_tab, idtable, 32 * sizeof(dbdev_tab_t)); + for (ret = 32; ret < DBDEV_TAB_SIZE; ret++) + dbdev_tab[ret].dev_id = ~0; + + dbdma_gptr->ddma_config = 0; + dbdma_gptr->ddma_throttle = 0; + dbdma_gptr->ddma_inten = 0xffff; + au_sync(); + + ret = request_irq(irq, dbdma_interrupt, 0, "dbdma", (void *)dbdma_gptr); + if (ret) + printk(KERN_ERR "Cannot grab DBDMA interrupt!\n"); + else { + dbdma_initialized = 1; + register_syscore_ops(&alchemy_dbdma_syscore_ops); + } + + return ret; +} + +static int __init alchemy_dbdma_init(void) +{ + switch (alchemy_get_cputype()) { + case ALCHEMY_CPU_AU1550: + return dbdma_setup(AU1550_DDMA_INT, au1550_dbdev_tab); + case ALCHEMY_CPU_AU1200: + return dbdma_setup(AU1200_DDMA_INT, au1200_dbdev_tab); + case ALCHEMY_CPU_AU1300: + return dbdma_setup(AU1300_DDMA_INT, au1300_dbdev_tab); + } + return 0; +} +subsys_initcall(alchemy_dbdma_init); diff --git a/arch/mips/alchemy/common/dma.c b/arch/mips/alchemy/common/dma.c index d6fbda232e6..9b624e2c0fc 100644 --- a/arch/mips/alchemy/common/dma.c +++ b/arch/mips/alchemy/common/dma.c @@ -29,6 +29,8 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. * */ + +#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -38,8 +40,6 @@ #include <asm/mach-au1x00/au1000.h> #include <asm/mach-au1x00/au1000_dma.h> -#if defined(CONFIG_SOC_AU1000) || defined(CONFIG_SOC_AU1500) || \ - defined(CONFIG_SOC_AU1100) /* * A note on resource allocation: * @@ -56,6 +56,9 @@ * returned from request_dma. */ +/* DMA Channel register block spacing */ +#define DMA_CHANNEL_LEN 0x00000100 + DEFINE_SPINLOCK(au1000_dma_spin_lock); struct dma_chan au1000_dma_table[NUM_AU1000_DMA_CHANNELS] = { @@ -75,22 +78,23 @@ static const struct dma_dev { unsigned int fifo_addr; unsigned int dma_mode; } dma_dev_table[DMA_NUM_DEV] = { - {UART0_ADDR + UART_TX, 0}, - {UART0_ADDR + UART_RX, 0}, - {0, 0}, - {0, 0}, - {AC97C_DATA, DMA_DW16 }, /* coherent */ - {AC97C_DATA, DMA_DR | DMA_DW16 }, /* coherent */ - {UART3_ADDR + UART_TX, DMA_DW8 | DMA_NC}, - {UART3_ADDR + UART_RX, DMA_DR | DMA_DW8 | DMA_NC}, - {USBD_EP0RD, DMA_DR | DMA_DW8 | DMA_NC}, - {USBD_EP0WR, DMA_DW8 | DMA_NC}, - {USBD_EP2WR, DMA_DW8 | DMA_NC}, - {USBD_EP3WR, DMA_DW8 | DMA_NC}, - {USBD_EP4RD, DMA_DR | DMA_DW8 | DMA_NC}, - {USBD_EP5RD, DMA_DR | DMA_DW8 | DMA_NC}, - {I2S_DATA, DMA_DW32 | DMA_NC}, - {I2S_DATA, DMA_DR | DMA_DW32 | DMA_NC} + { AU1000_UART0_PHYS_ADDR + 0x04, DMA_DW8 }, /* UART0_TX */ + { AU1000_UART0_PHYS_ADDR + 0x00, DMA_DW8 | DMA_DR }, /* UART0_RX */ + { 0, 0 }, /* DMA_REQ0 */ + { 0, 0 }, /* DMA_REQ1 */ + { AU1000_AC97_PHYS_ADDR + 0x08, DMA_DW16 }, /* AC97 TX c */ + { AU1000_AC97_PHYS_ADDR + 0x08, DMA_DW16 | DMA_DR }, /* AC97 RX c */ + { AU1000_UART3_PHYS_ADDR + 0x04, DMA_DW8 | DMA_NC }, /* UART3_TX */ + { AU1000_UART3_PHYS_ADDR + 0x00, DMA_DW8 | DMA_NC | DMA_DR }, /* UART3_RX */ + { AU1000_USB_UDC_PHYS_ADDR + 0x00, DMA_DW8 | DMA_NC | DMA_DR }, /* EP0RD */ + { AU1000_USB_UDC_PHYS_ADDR + 0x04, DMA_DW8 | DMA_NC }, /* EP0WR */ + { AU1000_USB_UDC_PHYS_ADDR + 0x08, DMA_DW8 | DMA_NC }, /* EP2WR */ + { AU1000_USB_UDC_PHYS_ADDR + 0x0c, DMA_DW8 | DMA_NC }, /* EP3WR */ + { AU1000_USB_UDC_PHYS_ADDR + 0x10, DMA_DW8 | DMA_NC | DMA_DR }, /* EP4RD */ + { AU1000_USB_UDC_PHYS_ADDR + 0x14, DMA_DW8 | DMA_NC | DMA_DR }, /* EP5RD */ + /* on Au1500, these 2 are DMA_REQ2/3 (GPIO208/209) instead! */ + { AU1000_I2S_PHYS_ADDR + 0x00, DMA_DW32 | DMA_NC}, /* I2S TX */ + { AU1000_I2S_PHYS_ADDR + 0x00, DMA_DW32 | DMA_NC | DMA_DR}, /* I2S RX */ }; int au1000_dma_read_proc(char *buf, char **start, off_t fpos, @@ -121,10 +125,10 @@ int au1000_dma_read_proc(char *buf, char **start, off_t fpos, /* Device FIFO addresses and default DMA modes - 2nd bank */ static const struct dma_dev dma_dev_table_bank2[DMA_NUM_DEV_BANK2] = { - { SD0_XMIT_FIFO, DMA_DS | DMA_DW8 }, /* coherent */ - { SD0_RECV_FIFO, DMA_DS | DMA_DR | DMA_DW8 }, /* coherent */ - { SD1_XMIT_FIFO, DMA_DS | DMA_DW8 }, /* coherent */ - { SD1_RECV_FIFO, DMA_DS | DMA_DR | DMA_DW8 } /* coherent */ + { AU1100_SD0_PHYS_ADDR + 0x00, DMA_DS | DMA_DW8 }, /* coherent */ + { AU1100_SD0_PHYS_ADDR + 0x04, DMA_DS | DMA_DW8 | DMA_DR }, /* coherent */ + { AU1100_SD1_PHYS_ADDR + 0x00, DMA_DS | DMA_DW8 }, /* coherent */ + { AU1100_SD1_PHYS_ADDR + 0x04, DMA_DS | DMA_DW8 | DMA_DR } /* coherent */ }; void dump_au1000_dma_channel(unsigned int dmanr) @@ -164,13 +168,13 @@ int request_au1000_dma(int dev_id, const char *dev_str, const struct dma_dev *dev; int i, ret; -#if defined(CONFIG_SOC_AU1100) - if (dev_id < 0 || dev_id >= (DMA_NUM_DEV + DMA_NUM_DEV_BANK2)) - return -EINVAL; -#else - if (dev_id < 0 || dev_id >= DMA_NUM_DEV) - return -EINVAL; -#endif + if (alchemy_get_cputype() == ALCHEMY_CPU_AU1100) { + if (dev_id < 0 || dev_id >= (DMA_NUM_DEV + DMA_NUM_DEV_BANK2)) + return -EINVAL; + } else { + if (dev_id < 0 || dev_id >= DMA_NUM_DEV) + return -EINVAL; + } for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) if (au1000_dma_table[i].dev_id < 0) @@ -188,22 +192,19 @@ int request_au1000_dma(int dev_id, const char *dev_str, dev = &dma_dev_table[dev_id]; if (irqhandler) { - chan->irq = AU1000_DMA_INT_BASE + i; chan->irq_dev = irq_dev_id; ret = request_irq(chan->irq, irqhandler, irqflags, dev_str, chan->irq_dev); if (ret) { - chan->irq = 0; chan->irq_dev = NULL; return ret; } } else { - chan->irq = 0; chan->irq_dev = NULL; } /* fill it in */ - chan->io = DMA_CHANNEL_BASE + i * DMA_CHANNEL_LEN; + chan->io = KSEG1ADDR(AU1000_DMA_PHYS_ADDR) + i * DMA_CHANNEL_LEN; chan->dev_id = dev_id; chan->dev_str = dev_str; chan->fifo_addr = dev->fifo_addr; @@ -226,13 +227,38 @@ void free_au1000_dma(unsigned int dmanr) } disable_dma(dmanr); - if (chan->irq) + if (chan->irq_dev) free_irq(chan->irq, chan->irq_dev); - chan->irq = 0; chan->irq_dev = NULL; chan->dev_id = -1; } EXPORT_SYMBOL(free_au1000_dma); -#endif /* AU1000 AU1500 AU1100 */ +static int __init au1000_dma_init(void) +{ + int base, i; + + switch (alchemy_get_cputype()) { + case ALCHEMY_CPU_AU1000: + base = AU1000_DMA_INT_BASE; + break; + case ALCHEMY_CPU_AU1500: + base = AU1500_DMA_INT_BASE; + break; + case ALCHEMY_CPU_AU1100: + base = AU1100_DMA_INT_BASE; + break; + default: + goto out; + } + + for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) + au1000_dma_table[i].irq = base + i; + + printk(KERN_INFO "Alchemy DMA initialized\n"); + +out: + return 0; +} +arch_initcall(au1000_dma_init); diff --git a/arch/mips/alchemy/common/gpio.c b/arch/mips/alchemy/common/gpio.c deleted file mode 100644 index e660ddd611c..00000000000 --- a/arch/mips/alchemy/common/gpio.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2007, OpenWrt.org, Florian Fainelli <florian@openwrt.org> - * Architecture specific GPIO support - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Notes : - * au1000 SoC have only one GPIO line : GPIO1 - * others have a second one : GPIO2 - */ - -#include <linux/module.h> - -#include <asm/mach-au1x00/au1000.h> -#include <asm/gpio.h> - -#define gpio1 sys -#if !defined(CONFIG_SOC_AU1000) - -static struct au1x00_gpio2 *const gpio2 = (struct au1x00_gpio2 *) GPIO2_BASE; -#define GPIO2_OUTPUT_ENABLE_MASK 0x00010000 - -static int au1xxx_gpio2_read(unsigned gpio) -{ - gpio -= AU1XXX_GPIO_BASE; - return ((gpio2->pinstate >> gpio) & 0x01); -} - -static void au1xxx_gpio2_write(unsigned gpio, int value) -{ - gpio -= AU1XXX_GPIO_BASE; - - gpio2->output = (GPIO2_OUTPUT_ENABLE_MASK << gpio) | ((!!value) << gpio); -} - -static int au1xxx_gpio2_direction_input(unsigned gpio) -{ - gpio -= AU1XXX_GPIO_BASE; - gpio2->dir &= ~(0x01 << gpio); - return 0; -} - -static int au1xxx_gpio2_direction_output(unsigned gpio, int value) -{ - gpio -= AU1XXX_GPIO_BASE; - gpio2->dir |= 0x01 << gpio; - gpio2->output = (GPIO2_OUTPUT_ENABLE_MASK << gpio) | ((!!value) << gpio); - return 0; -} - -#endif /* !defined(CONFIG_SOC_AU1000) */ - -static int au1xxx_gpio1_read(unsigned gpio) -{ - return (gpio1->pinstaterd >> gpio) & 0x01; -} - -static void au1xxx_gpio1_write(unsigned gpio, int value) -{ - if (value) - gpio1->outputset = (0x01 << gpio); - else - /* Output a zero */ - gpio1->outputclr = (0x01 << gpio); -} - -static int au1xxx_gpio1_direction_input(unsigned gpio) -{ - gpio1->pininputen = (0x01 << gpio); - return 0; -} - -static int au1xxx_gpio1_direction_output(unsigned gpio, int value) -{ - gpio1->trioutclr = (0x01 & gpio); - au1xxx_gpio1_write(gpio, value); - return 0; -} - -int au1xxx_gpio_get_value(unsigned gpio) -{ - if (gpio >= AU1XXX_GPIO_BASE) -#if defined(CONFIG_SOC_AU1000) - return 0; -#else - return au1xxx_gpio2_read(gpio); -#endif - else - return au1xxx_gpio1_read(gpio); -} -EXPORT_SYMBOL(au1xxx_gpio_get_value); - -void au1xxx_gpio_set_value(unsigned gpio, int value) -{ - if (gpio >= AU1XXX_GPIO_BASE) -#if defined(CONFIG_SOC_AU1000) - ; -#else - au1xxx_gpio2_write(gpio, value); -#endif - else - au1xxx_gpio1_write(gpio, value); -} -EXPORT_SYMBOL(au1xxx_gpio_set_value); - -int au1xxx_gpio_direction_input(unsigned gpio) -{ - if (gpio >= AU1XXX_GPIO_BASE) -#if defined(CONFIG_SOC_AU1000) - return -ENODEV; -#else - return au1xxx_gpio2_direction_input(gpio); -#endif - - return au1xxx_gpio1_direction_input(gpio); -} -EXPORT_SYMBOL(au1xxx_gpio_direction_input); - -int au1xxx_gpio_direction_output(unsigned gpio, int value) -{ - if (gpio >= AU1XXX_GPIO_BASE) -#if defined(CONFIG_SOC_AU1000) - return -ENODEV; -#else - return au1xxx_gpio2_direction_output(gpio, value); -#endif - - return au1xxx_gpio1_direction_output(gpio, value); -} -EXPORT_SYMBOL(au1xxx_gpio_direction_output); diff --git a/arch/mips/alchemy/common/gpiolib.c b/arch/mips/alchemy/common/gpiolib.c new file mode 100644 index 00000000000..f9bc4f52044 --- /dev/null +++ b/arch/mips/alchemy/common/gpiolib.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2007-2009, OpenWrt.org, Florian Fainelli <florian@openwrt.org> + * GPIOLIB support for Alchemy chips. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Notes : + * This file must ONLY be built when CONFIG_GPIOLIB=y and + * CONFIG_ALCHEMY_GPIO_INDIRECT=n, otherwise compilation will fail! + * au1000 SoC have only one GPIO block : GPIO1 + * Au1100, Au15x0, Au12x0 have a second one : GPIO2 + * Au1300 is totally different: 1 block with up to 128 GPIOs + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/gpio.h> +#include <asm/mach-au1x00/gpio-au1000.h> +#include <asm/mach-au1x00/gpio-au1300.h> + +static int gpio2_get(struct gpio_chip *chip, unsigned offset) +{ + return alchemy_gpio2_get_value(offset + ALCHEMY_GPIO2_BASE); +} + +static void gpio2_set(struct gpio_chip *chip, unsigned offset, int value) +{ + alchemy_gpio2_set_value(offset + ALCHEMY_GPIO2_BASE, value); +} + +static int gpio2_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return alchemy_gpio2_direction_input(offset + ALCHEMY_GPIO2_BASE); +} + +static int gpio2_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + return alchemy_gpio2_direction_output(offset + ALCHEMY_GPIO2_BASE, + value); +} + +static int gpio2_to_irq(struct gpio_chip *chip, unsigned offset) +{ + return alchemy_gpio2_to_irq(offset + ALCHEMY_GPIO2_BASE); +} + + +static int gpio1_get(struct gpio_chip *chip, unsigned offset) +{ + return alchemy_gpio1_get_value(offset + ALCHEMY_GPIO1_BASE); +} + +static void gpio1_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + alchemy_gpio1_set_value(offset + ALCHEMY_GPIO1_BASE, value); +} + +static int gpio1_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return alchemy_gpio1_direction_input(offset + ALCHEMY_GPIO1_BASE); +} + +static int gpio1_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + return alchemy_gpio1_direction_output(offset + ALCHEMY_GPIO1_BASE, + value); +} + +static int gpio1_to_irq(struct gpio_chip *chip, unsigned offset) +{ + return alchemy_gpio1_to_irq(offset + ALCHEMY_GPIO1_BASE); +} + +struct gpio_chip alchemy_gpio_chip[] = { + [0] = { + .label = "alchemy-gpio1", + .direction_input = gpio1_direction_input, + .direction_output = gpio1_direction_output, + .get = gpio1_get, + .set = gpio1_set, + .to_irq = gpio1_to_irq, + .base = ALCHEMY_GPIO1_BASE, + .ngpio = ALCHEMY_GPIO1_NUM, + }, + [1] = { + .label = "alchemy-gpio2", + .direction_input = gpio2_direction_input, + .direction_output = gpio2_direction_output, + .get = gpio2_get, + .set = gpio2_set, + .to_irq = gpio2_to_irq, + .base = ALCHEMY_GPIO2_BASE, + .ngpio = ALCHEMY_GPIO2_NUM, + }, +}; + +static int alchemy_gpic_get(struct gpio_chip *chip, unsigned int off) +{ + return au1300_gpio_get_value(off + AU1300_GPIO_BASE); +} + +static void alchemy_gpic_set(struct gpio_chip *chip, unsigned int off, int v) +{ + au1300_gpio_set_value(off + AU1300_GPIO_BASE, v); +} + +static int alchemy_gpic_dir_input(struct gpio_chip *chip, unsigned int off) +{ + return au1300_gpio_direction_input(off + AU1300_GPIO_BASE); +} + +static int alchemy_gpic_dir_output(struct gpio_chip *chip, unsigned int off, + int v) +{ + return au1300_gpio_direction_output(off + AU1300_GPIO_BASE, v); +} + +static int alchemy_gpic_gpio_to_irq(struct gpio_chip *chip, unsigned int off) +{ + return au1300_gpio_to_irq(off + AU1300_GPIO_BASE); +} + +static struct gpio_chip au1300_gpiochip = { + .label = "alchemy-gpic", + .direction_input = alchemy_gpic_dir_input, + .direction_output = alchemy_gpic_dir_output, + .get = alchemy_gpic_get, + .set = alchemy_gpic_set, + .to_irq = alchemy_gpic_gpio_to_irq, + .base = AU1300_GPIO_BASE, + .ngpio = AU1300_GPIO_NUM, +}; + +static int __init alchemy_gpiochip_init(void) +{ + int ret = 0; + + switch (alchemy_get_cputype()) { + case ALCHEMY_CPU_AU1000: + ret = gpiochip_add(&alchemy_gpio_chip[0]); + break; + case ALCHEMY_CPU_AU1500...ALCHEMY_CPU_AU1200: + ret = gpiochip_add(&alchemy_gpio_chip[0]); + ret |= gpiochip_add(&alchemy_gpio_chip[1]); + break; + case ALCHEMY_CPU_AU1300: + ret = gpiochip_add(&au1300_gpiochip); + break; + } + return ret; +} +arch_initcall(alchemy_gpiochip_init); diff --git a/arch/mips/alchemy/common/irq.c b/arch/mips/alchemy/common/irq.c index c88c821b4c3..63a71817a00 100644 --- a/arch/mips/alchemy/common/irq.c +++ b/arch/mips/alchemy/common/irq.c @@ -25,352 +25,377 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/bitops.h> +#include <linux/export.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/syscore_ops.h> #include <asm/irq_cpu.h> -#include <asm/mipsregs.h> #include <asm/mach-au1x00/au1000.h> -#ifdef CONFIG_MIPS_PB1000 -#include <asm/mach-pb1x00/pb1000.h> -#endif - -static int au1x_ic_settype(unsigned int irq, unsigned int flow_type); +#include <asm/mach-au1x00/gpio-au1300.h> + +/* Interrupt Controller register offsets */ +#define IC_CFG0RD 0x40 +#define IC_CFG0SET 0x40 +#define IC_CFG0CLR 0x44 +#define IC_CFG1RD 0x48 +#define IC_CFG1SET 0x48 +#define IC_CFG1CLR 0x4C +#define IC_CFG2RD 0x50 +#define IC_CFG2SET 0x50 +#define IC_CFG2CLR 0x54 +#define IC_REQ0INT 0x54 +#define IC_SRCRD 0x58 +#define IC_SRCSET 0x58 +#define IC_SRCCLR 0x5C +#define IC_REQ1INT 0x5C +#define IC_ASSIGNRD 0x60 +#define IC_ASSIGNSET 0x60 +#define IC_ASSIGNCLR 0x64 +#define IC_WAKERD 0x68 +#define IC_WAKESET 0x68 +#define IC_WAKECLR 0x6C +#define IC_MASKRD 0x70 +#define IC_MASKSET 0x70 +#define IC_MASKCLR 0x74 +#define IC_RISINGRD 0x78 +#define IC_RISINGCLR 0x78 +#define IC_FALLINGRD 0x7C +#define IC_FALLINGCLR 0x7C +#define IC_TESTBIT 0x80 /* per-processor fixed function irqs */ -struct au1xxx_irqmap au1xxx_ic0_map[] __initdata = { - -#if defined(CONFIG_SOC_AU1000) - { AU1000_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_UART2_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_SSI0_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_SSI1_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+1, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+2, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+3, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+4, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+5, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+6, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+7, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_TOY_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1 }, - { AU1000_RTC_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_IRDA_TX_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_IRDA_RX_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1000_ACSYNC_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_MAC1_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_AC97C_INT, IRQ_TYPE_EDGE_RISING, 0 }, - -#elif defined(CONFIG_SOC_AU1500) - - { AU1500_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_PCI_INTA, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1000_PCI_INTB, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1500_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_PCI_INTC, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1000_PCI_INTD, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1000_DMA_INT_BASE, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+1, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+2, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+3, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+4, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+5, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+6, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+7, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_TOY_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1 }, - { AU1000_RTC_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1000_ACSYNC_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1500_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1500_MAC1_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_AC97C_INT, IRQ_TYPE_EDGE_RISING, 0 }, - -#elif defined(CONFIG_SOC_AU1100) - - { AU1100_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1100_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1100_SD_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1100_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_SSI0_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_SSI1_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+1, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+2, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+3, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+4, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+5, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+6, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_DMA_INT_BASE+7, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_TOY_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1 }, - { AU1000_RTC_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_IRDA_TX_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_IRDA_RX_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1000_ACSYNC_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1100_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1100_LCD_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_AC97C_INT, IRQ_TYPE_EDGE_RISING, 0 }, - -#elif defined(CONFIG_SOC_AU1550) - - { AU1550_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1550_PCI_INTA, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1550_PCI_INTB, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1550_DDMA_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1550_CRYPTO_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1550_PCI_INTC, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1550_PCI_INTD, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1550_PCI_RST_INT, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1550_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1550_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1550_PSC0_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1550_PSC1_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1550_PSC2_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1550_PSC3_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_TOY_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1 }, - { AU1000_RTC_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1550_NAND_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1550_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1550_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1550_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 0 }, - { AU1550_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1550_MAC1_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - -#elif defined(CONFIG_SOC_AU1200) - - { AU1200_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_SWT_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1200_SD_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_DDMA_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_MAE_BE_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_MAE_FE_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_PSC0_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_PSC1_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_AES_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_CAMERA_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1000_TOY_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1 }, - { AU1000_RTC_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1000_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1200_NAND_INT, IRQ_TYPE_EDGE_RISING, 0 }, - { AU1200_USB_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_LCD_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - { AU1200_MAE_BOTH_INT, IRQ_TYPE_LEVEL_HIGH, 0 }, - -#else -#error "Error: Unknown Alchemy SOC" -#endif +struct alchemy_irqmap { + int irq; /* linux IRQ number */ + int type; /* IRQ_TYPE_ */ + int prio; /* irq priority, 0 highest, 3 lowest */ + int internal; /* GPIC: internal source (no ext. pin)? */ }; +static int au1x_ic_settype(struct irq_data *d, unsigned int type); +static int au1300_gpic_settype(struct irq_data *d, unsigned int type); -#ifdef CONFIG_PM -/* - * Save/restore the interrupt controller state. - * Called from the save/restore core registers as part of the - * au_sleep function in power.c.....maybe I should just pm_register() - * them instead? +/* NOTE on interrupt priorities: The original writers of this code said: + * + * Because of the tight timing of SETUP token to reply transactions, + * the USB devices-side packet complete interrupt (USB_DEV_REQ_INT) + * needs the highest priority. */ -static unsigned int sleep_intctl_config0[2]; -static unsigned int sleep_intctl_config1[2]; -static unsigned int sleep_intctl_config2[2]; -static unsigned int sleep_intctl_src[2]; -static unsigned int sleep_intctl_assign[2]; -static unsigned int sleep_intctl_wake[2]; -static unsigned int sleep_intctl_mask[2]; - -void save_au1xxx_intctl(void) -{ - sleep_intctl_config0[0] = au_readl(IC0_CFG0RD); - sleep_intctl_config1[0] = au_readl(IC0_CFG1RD); - sleep_intctl_config2[0] = au_readl(IC0_CFG2RD); - sleep_intctl_src[0] = au_readl(IC0_SRCRD); - sleep_intctl_assign[0] = au_readl(IC0_ASSIGNRD); - sleep_intctl_wake[0] = au_readl(IC0_WAKERD); - sleep_intctl_mask[0] = au_readl(IC0_MASKRD); - - sleep_intctl_config0[1] = au_readl(IC1_CFG0RD); - sleep_intctl_config1[1] = au_readl(IC1_CFG1RD); - sleep_intctl_config2[1] = au_readl(IC1_CFG2RD); - sleep_intctl_src[1] = au_readl(IC1_SRCRD); - sleep_intctl_assign[1] = au_readl(IC1_ASSIGNRD); - sleep_intctl_wake[1] = au_readl(IC1_WAKERD); - sleep_intctl_mask[1] = au_readl(IC1_MASKRD); +struct alchemy_irqmap au1000_irqmap[] __initdata = { + { AU1000_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_UART2_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_SSI0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_SSI1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_DMA_INT_BASE, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_DMA_INT_BASE+1, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_DMA_INT_BASE+2, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_DMA_INT_BASE+3, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_DMA_INT_BASE+4, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_DMA_INT_BASE+5, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_DMA_INT_BASE+6, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_DMA_INT_BASE+7, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1000_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1000_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1000_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1000_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1000_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1000_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1000_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 0 }, + { AU1000_IRDA_TX_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_IRDA_RX_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0, 0 }, + { AU1000_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1000_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1000_ACSYNC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1000_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_MAC1_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1000_AC97C_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { -1, }, +}; + +struct alchemy_irqmap au1500_irqmap[] __initdata = { + { AU1500_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_PCI_INTA, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1500_PCI_INTB, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1500_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_PCI_INTC, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1500_PCI_INTD, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1500_DMA_INT_BASE, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_DMA_INT_BASE+1, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_DMA_INT_BASE+2, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_DMA_INT_BASE+3, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_DMA_INT_BASE+4, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_DMA_INT_BASE+5, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_DMA_INT_BASE+6, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_DMA_INT_BASE+7, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1500_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1500_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1500_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1500_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1500_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1500_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1500_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 0 }, + { AU1500_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0, 0 }, + { AU1500_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1500_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1500_ACSYNC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1500_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_MAC1_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1500_AC97C_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { -1, }, +}; + +struct alchemy_irqmap au1100_irqmap[] __initdata = { + { AU1100_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_SD_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_SSI0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_SSI1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_DMA_INT_BASE, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_DMA_INT_BASE+1, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_DMA_INT_BASE+2, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_DMA_INT_BASE+3, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_DMA_INT_BASE+4, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_DMA_INT_BASE+5, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_DMA_INT_BASE+6, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_DMA_INT_BASE+7, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1100_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1100_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1100_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1100_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1100_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1100_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1100_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 0 }, + { AU1100_IRDA_TX_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_IRDA_RX_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0, 0 }, + { AU1100_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1100_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1100_ACSYNC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1100_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_LCD_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1100_AC97C_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { -1, }, +}; + +struct alchemy_irqmap au1550_irqmap[] __initdata = { + { AU1550_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1550_PCI_INTA, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1550_PCI_INTB, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1550_DDMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1550_CRYPTO_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1550_PCI_INTC, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1550_PCI_INTD, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1550_PCI_RST_INT, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1550_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1550_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1550_PSC0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1550_PSC1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1550_PSC2_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1550_PSC3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1550_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1550_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1550_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1550_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1550_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1550_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1550_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1550_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 0 }, + { AU1550_NAND_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1550_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0, 0 }, + { AU1550_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1550_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 1, 0 }, + { AU1550_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1550_MAC1_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { -1, }, +}; + +struct alchemy_irqmap au1200_irqmap[] __initdata = { + { AU1200_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_SWT_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1200_SD_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_DDMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_MAE_BE_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_MAE_FE_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_PSC0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_PSC1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_AES_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_CAMERA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1200_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1200_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1200_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1200_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1200_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1200_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1200_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 0 }, + { AU1200_NAND_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, + { AU1200_USB_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_LCD_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { AU1200_MAE_BOTH_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, + { -1, }, +}; + +static struct alchemy_irqmap au1300_irqmap[] __initdata = { + /* multifunction: gpio pin or device */ + { AU1300_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, + { AU1300_UART2_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, + { AU1300_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, + { AU1300_SD1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, + { AU1300_SD2_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, + { AU1300_PSC0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, + { AU1300_PSC1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, + { AU1300_PSC2_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, + { AU1300_PSC3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, + { AU1300_NAND_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, + /* au1300 internal */ + { AU1300_DDMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_MMU_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_MPU_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_GPU_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_UDMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, + { AU1300_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, + { AU1300_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, + { AU1300_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, + { AU1300_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, + { AU1300_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, + { AU1300_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, + { AU1300_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 1, }, + { AU1300_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_SD0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_USB_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_LCD_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_BSA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_MPE_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, + { AU1300_ITE_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_AES_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { AU1300_CIM_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, + { -1, }, /* terminator */ +}; + +/******************************************************************************/ + +static void au1x_ic0_unmask(struct irq_data *d) +{ + unsigned int bit = d->irq - AU1000_INTC0_INT_BASE; + void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); + + __raw_writel(1 << bit, base + IC_MASKSET); + __raw_writel(1 << bit, base + IC_WAKESET); + wmb(); } -/* - * For most restore operations, we clear the entire register and - * then set the bits we found during the save. - */ -void restore_au1xxx_intctl(void) -{ - au_writel(0xffffffff, IC0_MASKCLR); au_sync(); - - au_writel(0xffffffff, IC0_CFG0CLR); au_sync(); - au_writel(sleep_intctl_config0[0], IC0_CFG0SET); au_sync(); - au_writel(0xffffffff, IC0_CFG1CLR); au_sync(); - au_writel(sleep_intctl_config1[0], IC0_CFG1SET); au_sync(); - au_writel(0xffffffff, IC0_CFG2CLR); au_sync(); - au_writel(sleep_intctl_config2[0], IC0_CFG2SET); au_sync(); - au_writel(0xffffffff, IC0_SRCCLR); au_sync(); - au_writel(sleep_intctl_src[0], IC0_SRCSET); au_sync(); - au_writel(0xffffffff, IC0_ASSIGNCLR); au_sync(); - au_writel(sleep_intctl_assign[0], IC0_ASSIGNSET); au_sync(); - au_writel(0xffffffff, IC0_WAKECLR); au_sync(); - au_writel(sleep_intctl_wake[0], IC0_WAKESET); au_sync(); - au_writel(0xffffffff, IC0_RISINGCLR); au_sync(); - au_writel(0xffffffff, IC0_FALLINGCLR); au_sync(); - au_writel(0x00000000, IC0_TESTBIT); au_sync(); - - au_writel(0xffffffff, IC1_MASKCLR); au_sync(); - - au_writel(0xffffffff, IC1_CFG0CLR); au_sync(); - au_writel(sleep_intctl_config0[1], IC1_CFG0SET); au_sync(); - au_writel(0xffffffff, IC1_CFG1CLR); au_sync(); - au_writel(sleep_intctl_config1[1], IC1_CFG1SET); au_sync(); - au_writel(0xffffffff, IC1_CFG2CLR); au_sync(); - au_writel(sleep_intctl_config2[1], IC1_CFG2SET); au_sync(); - au_writel(0xffffffff, IC1_SRCCLR); au_sync(); - au_writel(sleep_intctl_src[1], IC1_SRCSET); au_sync(); - au_writel(0xffffffff, IC1_ASSIGNCLR); au_sync(); - au_writel(sleep_intctl_assign[1], IC1_ASSIGNSET); au_sync(); - au_writel(0xffffffff, IC1_WAKECLR); au_sync(); - au_writel(sleep_intctl_wake[1], IC1_WAKESET); au_sync(); - au_writel(0xffffffff, IC1_RISINGCLR); au_sync(); - au_writel(0xffffffff, IC1_FALLINGCLR); au_sync(); - au_writel(0x00000000, IC1_TESTBIT); au_sync(); - - au_writel(sleep_intctl_mask[1], IC1_MASKSET); au_sync(); - - au_writel(sleep_intctl_mask[0], IC0_MASKSET); au_sync(); -} -#endif /* CONFIG_PM */ - - -static void au1x_ic0_unmask(unsigned int irq_nr) -{ - unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE; - au_writel(1 << bit, IC0_MASKSET); - au_writel(1 << bit, IC0_WAKESET); - au_sync(); -} - -static void au1x_ic1_unmask(unsigned int irq_nr) -{ - unsigned int bit = irq_nr - AU1000_INTC1_INT_BASE; - au_writel(1 << bit, IC1_MASKSET); - au_writel(1 << bit, IC1_WAKESET); - -/* very hacky. does the pb1000 cpld auto-disable this int? - * nowhere in the current kernel sources is it disabled. --mlau - */ -#if defined(CONFIG_MIPS_PB1000) - if (irq_nr == AU1000_GPIO_15) - au_writel(0x4000, PB1000_MDR); /* enable int */ -#endif - au_sync(); +static void au1x_ic1_unmask(struct irq_data *d) +{ + unsigned int bit = d->irq - AU1000_INTC1_INT_BASE; + void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); + + __raw_writel(1 << bit, base + IC_MASKSET); + __raw_writel(1 << bit, base + IC_WAKESET); + wmb(); } -static void au1x_ic0_mask(unsigned int irq_nr) +static void au1x_ic0_mask(struct irq_data *d) { - unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE; - au_writel(1 << bit, IC0_MASKCLR); - au_writel(1 << bit, IC0_WAKECLR); - au_sync(); + unsigned int bit = d->irq - AU1000_INTC0_INT_BASE; + void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); + + __raw_writel(1 << bit, base + IC_MASKCLR); + __raw_writel(1 << bit, base + IC_WAKECLR); + wmb(); } -static void au1x_ic1_mask(unsigned int irq_nr) +static void au1x_ic1_mask(struct irq_data *d) { - unsigned int bit = irq_nr - AU1000_INTC1_INT_BASE; - au_writel(1 << bit, IC1_MASKCLR); - au_writel(1 << bit, IC1_WAKECLR); - au_sync(); + unsigned int bit = d->irq - AU1000_INTC1_INT_BASE; + void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); + + __raw_writel(1 << bit, base + IC_MASKCLR); + __raw_writel(1 << bit, base + IC_WAKECLR); + wmb(); } -static void au1x_ic0_ack(unsigned int irq_nr) +static void au1x_ic0_ack(struct irq_data *d) { - unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE; + unsigned int bit = d->irq - AU1000_INTC0_INT_BASE; + void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); /* * This may assume that we don't get interrupts from * both edges at once, or if we do, that we don't care. */ - au_writel(1 << bit, IC0_FALLINGCLR); - au_writel(1 << bit, IC0_RISINGCLR); - au_sync(); + __raw_writel(1 << bit, base + IC_FALLINGCLR); + __raw_writel(1 << bit, base + IC_RISINGCLR); + wmb(); } -static void au1x_ic1_ack(unsigned int irq_nr) +static void au1x_ic1_ack(struct irq_data *d) { - unsigned int bit = irq_nr - AU1000_INTC1_INT_BASE; + unsigned int bit = d->irq - AU1000_INTC1_INT_BASE; + void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); /* * This may assume that we don't get interrupts from * both edges at once, or if we do, that we don't care. */ - au_writel(1 << bit, IC1_FALLINGCLR); - au_writel(1 << bit, IC1_RISINGCLR); - au_sync(); + __raw_writel(1 << bit, base + IC_FALLINGCLR); + __raw_writel(1 << bit, base + IC_RISINGCLR); + wmb(); +} + +static void au1x_ic0_maskack(struct irq_data *d) +{ + unsigned int bit = d->irq - AU1000_INTC0_INT_BASE; + void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); + + __raw_writel(1 << bit, base + IC_WAKECLR); + __raw_writel(1 << bit, base + IC_MASKCLR); + __raw_writel(1 << bit, base + IC_RISINGCLR); + __raw_writel(1 << bit, base + IC_FALLINGCLR); + wmb(); +} + +static void au1x_ic1_maskack(struct irq_data *d) +{ + unsigned int bit = d->irq - AU1000_INTC1_INT_BASE; + void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); + + __raw_writel(1 << bit, base + IC_WAKECLR); + __raw_writel(1 << bit, base + IC_MASKCLR); + __raw_writel(1 << bit, base + IC_RISINGCLR); + __raw_writel(1 << bit, base + IC_FALLINGCLR); + wmb(); } -static int au1x_ic1_setwake(unsigned int irq, unsigned int on) +static int au1x_ic1_setwake(struct irq_data *d, unsigned int on) { - unsigned int bit = irq - AU1000_INTC1_INT_BASE; + int bit = d->irq - AU1000_INTC1_INT_BASE; unsigned long wakemsk, flags; - /* only GPIO 0-7 can act as wakeup source: */ - if ((irq < AU1000_GPIO_0) || (irq > AU1000_GPIO_7)) + /* only GPIO 0-7 can act as wakeup source. Fortunately these + * are wired up identically on all supported variants. + */ + if ((bit < 0) || (bit > 7)) return -EINVAL; local_irq_save(flags); - wakemsk = au_readl(SYS_WAKEMSK); + wakemsk = __raw_readl((void __iomem *)SYS_WAKEMSK); if (on) wakemsk |= 1 << bit; else wakemsk &= ~(1 << bit); - au_writel(wakemsk, SYS_WAKEMSK); - au_sync(); + __raw_writel(wakemsk, (void __iomem *)SYS_WAKEMSK); + wmb(); local_irq_restore(flags); return 0; @@ -379,233 +404,594 @@ static int au1x_ic1_setwake(unsigned int irq, unsigned int on) /* * irq_chips for both ICs; this way the mask handlers can be * as short as possible. - * - * NOTE: the ->ack() callback is used by the handle_edge_irq - * flowhandler only, the ->mask_ack() one by handle_level_irq, - * so no need for an irq_chip for each type of irq (level/edge). */ static struct irq_chip au1x_ic0_chip = { .name = "Alchemy-IC0", - .ack = au1x_ic0_ack, /* edge */ - .mask = au1x_ic0_mask, - .mask_ack = au1x_ic0_mask, /* level */ - .unmask = au1x_ic0_unmask, - .set_type = au1x_ic_settype, + .irq_ack = au1x_ic0_ack, + .irq_mask = au1x_ic0_mask, + .irq_mask_ack = au1x_ic0_maskack, + .irq_unmask = au1x_ic0_unmask, + .irq_set_type = au1x_ic_settype, }; static struct irq_chip au1x_ic1_chip = { .name = "Alchemy-IC1", - .ack = au1x_ic1_ack, /* edge */ - .mask = au1x_ic1_mask, - .mask_ack = au1x_ic1_mask, /* level */ - .unmask = au1x_ic1_unmask, - .set_type = au1x_ic_settype, - .set_wake = au1x_ic1_setwake, + .irq_ack = au1x_ic1_ack, + .irq_mask = au1x_ic1_mask, + .irq_mask_ack = au1x_ic1_maskack, + .irq_unmask = au1x_ic1_unmask, + .irq_set_type = au1x_ic_settype, + .irq_set_wake = au1x_ic1_setwake, }; -static int au1x_ic_settype(unsigned int irq, unsigned int flow_type) +static int au1x_ic_settype(struct irq_data *d, unsigned int flow_type) { struct irq_chip *chip; - unsigned long icr[6]; - unsigned int bit, ic; + unsigned int bit, irq = d->irq; + irq_flow_handler_t handler = NULL; + unsigned char *name = NULL; + void __iomem *base; int ret; if (irq >= AU1000_INTC1_INT_BASE) { bit = irq - AU1000_INTC1_INT_BASE; chip = &au1x_ic1_chip; - ic = 1; + base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); } else { bit = irq - AU1000_INTC0_INT_BASE; chip = &au1x_ic0_chip; - ic = 0; + base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); } if (bit > 31) return -EINVAL; - icr[0] = ic ? IC1_CFG0SET : IC0_CFG0SET; - icr[1] = ic ? IC1_CFG1SET : IC0_CFG1SET; - icr[2] = ic ? IC1_CFG2SET : IC0_CFG2SET; - icr[3] = ic ? IC1_CFG0CLR : IC0_CFG0CLR; - icr[4] = ic ? IC1_CFG1CLR : IC0_CFG1CLR; - icr[5] = ic ? IC1_CFG2CLR : IC0_CFG2CLR; - ret = 0; switch (flow_type) { /* cfgregs 2:1:0 */ case IRQ_TYPE_EDGE_RISING: /* 0:0:1 */ - au_writel(1 << bit, icr[5]); - au_writel(1 << bit, icr[4]); - au_writel(1 << bit, icr[0]); - set_irq_chip_and_handler_name(irq, chip, - handle_edge_irq, "riseedge"); + __raw_writel(1 << bit, base + IC_CFG2CLR); + __raw_writel(1 << bit, base + IC_CFG1CLR); + __raw_writel(1 << bit, base + IC_CFG0SET); + handler = handle_edge_irq; + name = "riseedge"; break; case IRQ_TYPE_EDGE_FALLING: /* 0:1:0 */ - au_writel(1 << bit, icr[5]); - au_writel(1 << bit, icr[1]); - au_writel(1 << bit, icr[3]); - set_irq_chip_and_handler_name(irq, chip, - handle_edge_irq, "falledge"); + __raw_writel(1 << bit, base + IC_CFG2CLR); + __raw_writel(1 << bit, base + IC_CFG1SET); + __raw_writel(1 << bit, base + IC_CFG0CLR); + handler = handle_edge_irq; + name = "falledge"; break; case IRQ_TYPE_EDGE_BOTH: /* 0:1:1 */ - au_writel(1 << bit, icr[5]); - au_writel(1 << bit, icr[1]); - au_writel(1 << bit, icr[0]); - set_irq_chip_and_handler_name(irq, chip, - handle_edge_irq, "bothedge"); + __raw_writel(1 << bit, base + IC_CFG2CLR); + __raw_writel(1 << bit, base + IC_CFG1SET); + __raw_writel(1 << bit, base + IC_CFG0SET); + handler = handle_edge_irq; + name = "bothedge"; break; case IRQ_TYPE_LEVEL_HIGH: /* 1:0:1 */ - au_writel(1 << bit, icr[2]); - au_writel(1 << bit, icr[4]); - au_writel(1 << bit, icr[0]); - set_irq_chip_and_handler_name(irq, chip, - handle_level_irq, "hilevel"); + __raw_writel(1 << bit, base + IC_CFG2SET); + __raw_writel(1 << bit, base + IC_CFG1CLR); + __raw_writel(1 << bit, base + IC_CFG0SET); + handler = handle_level_irq; + name = "hilevel"; break; case IRQ_TYPE_LEVEL_LOW: /* 1:1:0 */ - au_writel(1 << bit, icr[2]); - au_writel(1 << bit, icr[1]); - au_writel(1 << bit, icr[3]); - set_irq_chip_and_handler_name(irq, chip, - handle_level_irq, "lowlevel"); + __raw_writel(1 << bit, base + IC_CFG2SET); + __raw_writel(1 << bit, base + IC_CFG1SET); + __raw_writel(1 << bit, base + IC_CFG0CLR); + handler = handle_level_irq; + name = "lowlevel"; break; case IRQ_TYPE_NONE: /* 0:0:0 */ - au_writel(1 << bit, icr[5]); - au_writel(1 << bit, icr[4]); - au_writel(1 << bit, icr[3]); - /* set at least chip so we can call set_irq_type() on it */ - set_irq_chip(irq, chip); + __raw_writel(1 << bit, base + IC_CFG2CLR); + __raw_writel(1 << bit, base + IC_CFG1CLR); + __raw_writel(1 << bit, base + IC_CFG0CLR); break; default: ret = -EINVAL; } - au_sync(); + __irq_set_chip_handler_name_locked(d->irq, chip, handler, name); + + wmb(); return ret; } -asmlinkage void plat_irq_dispatch(void) +/******************************************************************************/ + +/* + * au1300_gpic_chgcfg - change PIN configuration. + * @gpio: pin to change (0-based GPIO number from datasheet). + * @clr: clear all bits set in 'clr'. + * @set: set these bits. + * + * modifies a pins' configuration register, bits set in @clr will + * be cleared in the register, bits in @set will be set. + */ +static inline void au1300_gpic_chgcfg(unsigned int gpio, + unsigned long clr, + unsigned long set) { - unsigned int pending = read_c0_status() & read_c0_cause(); - unsigned long s, off, bit; - - if (pending & CAUSEF_IP7) { - do_IRQ(MIPS_CPU_IRQ_BASE + 7); - return; - } else if (pending & CAUSEF_IP2) { - s = IC0_REQ0INT; - off = AU1000_INTC0_INT_BASE; - } else if (pending & CAUSEF_IP3) { - s = IC0_REQ1INT; - off = AU1000_INTC0_INT_BASE; - } else if (pending & CAUSEF_IP4) { - s = IC1_REQ0INT; - off = AU1000_INTC1_INT_BASE; - } else if (pending & CAUSEF_IP5) { - s = IC1_REQ1INT; - off = AU1000_INTC1_INT_BASE; - } else - goto spurious; - - bit = 0; - s = au_readl(s); - if (unlikely(!s)) { -spurious: - spurious_interrupt(); - return; - } -#ifdef AU1000_USB_DEV_REQ_INT - /* - * Because of the tight timing of SETUP token to reply - * transactions, the USB devices-side packet complete - * interrupt needs the highest priority. - */ - bit = 1 << (AU1000_USB_DEV_REQ_INT - AU1000_INTC0_INT_BASE); - if ((pending & CAUSEF_IP2) && (s & bit)) { - do_IRQ(AU1000_USB_DEV_REQ_INT); - return; + void __iomem *r = AU1300_GPIC_ADDR; + unsigned long l; + + r += gpio * 4; /* offset into pin config array */ + l = __raw_readl(r + AU1300_GPIC_PINCFG); + l &= ~clr; + l |= set; + __raw_writel(l, r + AU1300_GPIC_PINCFG); + wmb(); +} + +/* + * au1300_pinfunc_to_gpio - assign a pin as GPIO input (GPIO ctrl). + * @pin: pin (0-based GPIO number from datasheet). + * + * Assigns a GPIO pin to the GPIO controller, so its level can either + * be read or set through the generic GPIO functions. + * If you need a GPOUT, use au1300_gpio_set_value(pin, 0/1). + * REVISIT: is this function really necessary? + */ +void au1300_pinfunc_to_gpio(enum au1300_multifunc_pins gpio) +{ + au1300_gpio_direction_input(gpio + AU1300_GPIO_BASE); +} +EXPORT_SYMBOL_GPL(au1300_pinfunc_to_gpio); + +/* + * au1300_pinfunc_to_dev - assign a pin to the device function. + * @pin: pin (0-based GPIO number from datasheet). + * + * Assigns a GPIO pin to its associated device function; the pin will be + * driven by the device and not through GPIO functions. + */ +void au1300_pinfunc_to_dev(enum au1300_multifunc_pins gpio) +{ + void __iomem *r = AU1300_GPIC_ADDR; + unsigned long bit; + + r += GPIC_GPIO_BANKOFF(gpio); + bit = GPIC_GPIO_TO_BIT(gpio); + __raw_writel(bit, r + AU1300_GPIC_DEVSEL); + wmb(); +} +EXPORT_SYMBOL_GPL(au1300_pinfunc_to_dev); + +/* + * au1300_set_irq_priority - set internal priority of IRQ. + * @irq: irq to set priority (linux irq number). + * @p: priority (0 = highest, 3 = lowest). + */ +void au1300_set_irq_priority(unsigned int irq, int p) +{ + irq -= ALCHEMY_GPIC_INT_BASE; + au1300_gpic_chgcfg(irq, GPIC_CFG_IL_MASK, GPIC_CFG_IL_SET(p)); +} +EXPORT_SYMBOL_GPL(au1300_set_irq_priority); + +/* + * au1300_set_dbdma_gpio - assign a gpio to one of the DBDMA triggers. + * @dchan: dbdma trigger select (0, 1). + * @gpio: pin to assign as trigger. + * + * DBDMA controller has 2 external trigger sources; this function + * assigns a GPIO to the selected trigger. + */ +void au1300_set_dbdma_gpio(int dchan, unsigned int gpio) +{ + unsigned long r; + + if ((dchan >= 0) && (dchan <= 1)) { + r = __raw_readl(AU1300_GPIC_ADDR + AU1300_GPIC_DMASEL); + r &= ~(0xff << (8 * dchan)); + r |= (gpio & 0x7f) << (8 * dchan); + __raw_writel(r, AU1300_GPIC_ADDR + AU1300_GPIC_DMASEL); + wmb(); } -#endif - do_IRQ(__ffs(s) + off); } -/* setup edge/level and assign request 0/1 */ -void __init au1xxx_setup_irqmap(struct au1xxx_irqmap *map, int count) +static inline void gpic_pin_set_idlewake(unsigned int gpio, int allow) { - unsigned int bit, irq_nr; + au1300_gpic_chgcfg(gpio, GPIC_CFG_IDLEWAKE, + allow ? GPIC_CFG_IDLEWAKE : 0); +} - while (count--) { - irq_nr = map[count].im_irq; +static void au1300_gpic_mask(struct irq_data *d) +{ + void __iomem *r = AU1300_GPIC_ADDR; + unsigned long bit, irq = d->irq; - if (((irq_nr < AU1000_INTC0_INT_BASE) || - (irq_nr >= AU1000_INTC0_INT_BASE + 32)) && - ((irq_nr < AU1000_INTC1_INT_BASE) || - (irq_nr >= AU1000_INTC1_INT_BASE + 32))) - continue; + irq -= ALCHEMY_GPIC_INT_BASE; + r += GPIC_GPIO_BANKOFF(irq); + bit = GPIC_GPIO_TO_BIT(irq); + __raw_writel(bit, r + AU1300_GPIC_IDIS); + wmb(); - if (irq_nr >= AU1000_INTC1_INT_BASE) { - bit = irq_nr - AU1000_INTC1_INT_BASE; - if (map[count].im_request) - au_writel(1 << bit, IC1_ASSIGNCLR); - } else { - bit = irq_nr - AU1000_INTC0_INT_BASE; - if (map[count].im_request) - au_writel(1 << bit, IC0_ASSIGNCLR); - } + gpic_pin_set_idlewake(irq, 0); +} + +static void au1300_gpic_unmask(struct irq_data *d) +{ + void __iomem *r = AU1300_GPIC_ADDR; + unsigned long bit, irq = d->irq; + + irq -= ALCHEMY_GPIC_INT_BASE; + + gpic_pin_set_idlewake(irq, 1); + + r += GPIC_GPIO_BANKOFF(irq); + bit = GPIC_GPIO_TO_BIT(irq); + __raw_writel(bit, r + AU1300_GPIC_IEN); + wmb(); +} + +static void au1300_gpic_maskack(struct irq_data *d) +{ + void __iomem *r = AU1300_GPIC_ADDR; + unsigned long bit, irq = d->irq; - au1x_ic_settype(irq_nr, map[count].im_type); + irq -= ALCHEMY_GPIC_INT_BASE; + r += GPIC_GPIO_BANKOFF(irq); + bit = GPIC_GPIO_TO_BIT(irq); + __raw_writel(bit, r + AU1300_GPIC_IPEND); /* ack */ + __raw_writel(bit, r + AU1300_GPIC_IDIS); /* mask */ + wmb(); + + gpic_pin_set_idlewake(irq, 0); +} + +static void au1300_gpic_ack(struct irq_data *d) +{ + void __iomem *r = AU1300_GPIC_ADDR; + unsigned long bit, irq = d->irq; + + irq -= ALCHEMY_GPIC_INT_BASE; + r += GPIC_GPIO_BANKOFF(irq); + bit = GPIC_GPIO_TO_BIT(irq); + __raw_writel(bit, r + AU1300_GPIC_IPEND); /* ack */ + wmb(); +} + +static struct irq_chip au1300_gpic = { + .name = "GPIOINT", + .irq_ack = au1300_gpic_ack, + .irq_mask = au1300_gpic_mask, + .irq_mask_ack = au1300_gpic_maskack, + .irq_unmask = au1300_gpic_unmask, + .irq_set_type = au1300_gpic_settype, +}; + +static int au1300_gpic_settype(struct irq_data *d, unsigned int type) +{ + unsigned long s; + unsigned char *name = NULL; + irq_flow_handler_t hdl = NULL; + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + s = GPIC_CFG_IC_LEVEL_HIGH; + name = "high"; + hdl = handle_level_irq; + break; + case IRQ_TYPE_LEVEL_LOW: + s = GPIC_CFG_IC_LEVEL_LOW; + name = "low"; + hdl = handle_level_irq; + break; + case IRQ_TYPE_EDGE_RISING: + s = GPIC_CFG_IC_EDGE_RISE; + name = "posedge"; + hdl = handle_edge_irq; + break; + case IRQ_TYPE_EDGE_FALLING: + s = GPIC_CFG_IC_EDGE_FALL; + name = "negedge"; + hdl = handle_edge_irq; + break; + case IRQ_TYPE_EDGE_BOTH: + s = GPIC_CFG_IC_EDGE_BOTH; + name = "bothedge"; + hdl = handle_edge_irq; + break; + case IRQ_TYPE_NONE: + s = GPIC_CFG_IC_OFF; + name = "disabled"; + hdl = handle_level_irq; + break; + default: + return -EINVAL; } + + __irq_set_chip_handler_name_locked(d->irq, &au1300_gpic, hdl, name); + + au1300_gpic_chgcfg(d->irq - ALCHEMY_GPIC_INT_BASE, GPIC_CFG_IC_MASK, s); + + return 0; } -void __init arch_init_irq(void) +/******************************************************************************/ + +static inline void ic_init(void __iomem *base) +{ + /* initialize interrupt controller to a safe state */ + __raw_writel(0xffffffff, base + IC_CFG0CLR); + __raw_writel(0xffffffff, base + IC_CFG1CLR); + __raw_writel(0xffffffff, base + IC_CFG2CLR); + __raw_writel(0xffffffff, base + IC_MASKCLR); + __raw_writel(0xffffffff, base + IC_ASSIGNCLR); + __raw_writel(0xffffffff, base + IC_WAKECLR); + __raw_writel(0xffffffff, base + IC_SRCSET); + __raw_writel(0xffffffff, base + IC_FALLINGCLR); + __raw_writel(0xffffffff, base + IC_RISINGCLR); + __raw_writel(0x00000000, base + IC_TESTBIT); + wmb(); +} + +static unsigned long alchemy_gpic_pmdata[ALCHEMY_GPIC_INT_NUM + 6]; + +static inline void alchemy_ic_suspend_one(void __iomem *base, unsigned long *d) +{ + d[0] = __raw_readl(base + IC_CFG0RD); + d[1] = __raw_readl(base + IC_CFG1RD); + d[2] = __raw_readl(base + IC_CFG2RD); + d[3] = __raw_readl(base + IC_SRCRD); + d[4] = __raw_readl(base + IC_ASSIGNRD); + d[5] = __raw_readl(base + IC_WAKERD); + d[6] = __raw_readl(base + IC_MASKRD); + ic_init(base); /* shut it up too while at it */ +} + +static inline void alchemy_ic_resume_one(void __iomem *base, unsigned long *d) +{ + ic_init(base); + + __raw_writel(d[0], base + IC_CFG0SET); + __raw_writel(d[1], base + IC_CFG1SET); + __raw_writel(d[2], base + IC_CFG2SET); + __raw_writel(d[3], base + IC_SRCSET); + __raw_writel(d[4], base + IC_ASSIGNSET); + __raw_writel(d[5], base + IC_WAKESET); + wmb(); + + __raw_writel(d[6], base + IC_MASKSET); + wmb(); +} + +static int alchemy_ic_suspend(void) +{ + alchemy_ic_suspend_one((void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR), + alchemy_gpic_pmdata); + alchemy_ic_suspend_one((void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR), + &alchemy_gpic_pmdata[7]); + return 0; +} + +static void alchemy_ic_resume(void) +{ + alchemy_ic_resume_one((void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR), + &alchemy_gpic_pmdata[7]); + alchemy_ic_resume_one((void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR), + alchemy_gpic_pmdata); +} + +static int alchemy_gpic_suspend(void) { + void __iomem *base = (void __iomem *)KSEG1ADDR(AU1300_GPIC_PHYS_ADDR); int i; - /* - * Initialize interrupt controllers to a safe state. - */ - au_writel(0xffffffff, IC0_CFG0CLR); - au_writel(0xffffffff, IC0_CFG1CLR); - au_writel(0xffffffff, IC0_CFG2CLR); - au_writel(0xffffffff, IC0_MASKCLR); - au_writel(0xffffffff, IC0_ASSIGNSET); - au_writel(0xffffffff, IC0_WAKECLR); - au_writel(0xffffffff, IC0_SRCSET); - au_writel(0xffffffff, IC0_FALLINGCLR); - au_writel(0xffffffff, IC0_RISINGCLR); - au_writel(0x00000000, IC0_TESTBIT); - - au_writel(0xffffffff, IC1_CFG0CLR); - au_writel(0xffffffff, IC1_CFG1CLR); - au_writel(0xffffffff, IC1_CFG2CLR); - au_writel(0xffffffff, IC1_MASKCLR); - au_writel(0xffffffff, IC1_ASSIGNSET); - au_writel(0xffffffff, IC1_WAKECLR); - au_writel(0xffffffff, IC1_SRCSET); - au_writel(0xffffffff, IC1_FALLINGCLR); - au_writel(0xffffffff, IC1_RISINGCLR); - au_writel(0x00000000, IC1_TESTBIT); + /* save 4 interrupt mask status registers */ + alchemy_gpic_pmdata[0] = __raw_readl(base + AU1300_GPIC_IEN + 0x0); + alchemy_gpic_pmdata[1] = __raw_readl(base + AU1300_GPIC_IEN + 0x4); + alchemy_gpic_pmdata[2] = __raw_readl(base + AU1300_GPIC_IEN + 0x8); + alchemy_gpic_pmdata[3] = __raw_readl(base + AU1300_GPIC_IEN + 0xc); + + /* save misc register(s) */ + alchemy_gpic_pmdata[4] = __raw_readl(base + AU1300_GPIC_DMASEL); + + /* molto silenzioso */ + __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x0); + __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x4); + __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x8); + __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0xc); + wmb(); + + /* save pin/int-type configuration */ + base += AU1300_GPIC_PINCFG; + for (i = 0; i < ALCHEMY_GPIC_INT_NUM; i++) + alchemy_gpic_pmdata[i + 5] = __raw_readl(base + (i << 2)); + + wmb(); + + return 0; +} + +static void alchemy_gpic_resume(void) +{ + void __iomem *base = (void __iomem *)KSEG1ADDR(AU1300_GPIC_PHYS_ADDR); + int i; + + /* disable all first */ + __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x0); + __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x4); + __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x8); + __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0xc); + wmb(); + + /* restore pin/int-type configurations */ + base += AU1300_GPIC_PINCFG; + for (i = 0; i < ALCHEMY_GPIC_INT_NUM; i++) + __raw_writel(alchemy_gpic_pmdata[i + 5], base + (i << 2)); + wmb(); + + /* restore misc register(s) */ + base = (void __iomem *)KSEG1ADDR(AU1300_GPIC_PHYS_ADDR); + __raw_writel(alchemy_gpic_pmdata[4], base + AU1300_GPIC_DMASEL); + wmb(); + + /* finally restore masks */ + __raw_writel(alchemy_gpic_pmdata[0], base + AU1300_GPIC_IEN + 0x0); + __raw_writel(alchemy_gpic_pmdata[1], base + AU1300_GPIC_IEN + 0x4); + __raw_writel(alchemy_gpic_pmdata[2], base + AU1300_GPIC_IEN + 0x8); + __raw_writel(alchemy_gpic_pmdata[3], base + AU1300_GPIC_IEN + 0xc); + wmb(); +} + +static struct syscore_ops alchemy_ic_pmops = { + .suspend = alchemy_ic_suspend, + .resume = alchemy_ic_resume, +}; + +static struct syscore_ops alchemy_gpic_pmops = { + .suspend = alchemy_gpic_suspend, + .resume = alchemy_gpic_resume, +}; + +/******************************************************************************/ + +/* create chained handlers for the 4 IC requests to the MIPS IRQ ctrl */ +#define DISP(name, base, addr) \ +static void au1000_##name##_dispatch(unsigned int irq, struct irq_desc *d) \ +{ \ + unsigned long r = __raw_readl((void __iomem *)KSEG1ADDR(addr)); \ + if (likely(r)) \ + generic_handle_irq(base + __ffs(r)); \ + else \ + spurious_interrupt(); \ +} +DISP(ic0r0, AU1000_INTC0_INT_BASE, AU1000_IC0_PHYS_ADDR + IC_REQ0INT) +DISP(ic0r1, AU1000_INTC0_INT_BASE, AU1000_IC0_PHYS_ADDR + IC_REQ1INT) +DISP(ic1r0, AU1000_INTC1_INT_BASE, AU1000_IC1_PHYS_ADDR + IC_REQ0INT) +DISP(ic1r1, AU1000_INTC1_INT_BASE, AU1000_IC1_PHYS_ADDR + IC_REQ1INT) + +static void alchemy_gpic_dispatch(unsigned int irq, struct irq_desc *d) +{ + int i = __raw_readl(AU1300_GPIC_ADDR + AU1300_GPIC_PRIENC); + generic_handle_irq(ALCHEMY_GPIC_INT_BASE + i); +} + +/******************************************************************************/ + +static void __init au1000_init_irq(struct alchemy_irqmap *map) +{ + unsigned int bit, irq_nr; + void __iomem *base; + + ic_init((void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR)); + ic_init((void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR)); + register_syscore_ops(&alchemy_ic_pmops); mips_cpu_irq_init(); /* register all 64 possible IC0+IC1 irq sources as type "none". * Use set_irq_type() to set edge/level behaviour at runtime. */ - for (i = AU1000_INTC0_INT_BASE; - (i < AU1000_INTC0_INT_BASE + 32); i++) - au1x_ic_settype(i, IRQ_TYPE_NONE); + for (irq_nr = AU1000_INTC0_INT_BASE; + (irq_nr < AU1000_INTC0_INT_BASE + 32); irq_nr++) + au1x_ic_settype(irq_get_irq_data(irq_nr), IRQ_TYPE_NONE); - for (i = AU1000_INTC1_INT_BASE; - (i < AU1000_INTC1_INT_BASE + 32); i++) - au1x_ic_settype(i, IRQ_TYPE_NONE); + for (irq_nr = AU1000_INTC1_INT_BASE; + (irq_nr < AU1000_INTC1_INT_BASE + 32); irq_nr++) + au1x_ic_settype(irq_get_irq_data(irq_nr), IRQ_TYPE_NONE); /* * Initialize IC0, which is fixed per processor. */ - au1xxx_setup_irqmap(au1xxx_ic0_map, ARRAY_SIZE(au1xxx_ic0_map)); + while (map->irq != -1) { + irq_nr = map->irq; + + if (irq_nr >= AU1000_INTC1_INT_BASE) { + bit = irq_nr - AU1000_INTC1_INT_BASE; + base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); + } else { + bit = irq_nr - AU1000_INTC0_INT_BASE; + base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); + } + if (map->prio == 0) + __raw_writel(1 << bit, base + IC_ASSIGNSET); + + au1x_ic_settype(irq_get_irq_data(irq_nr), map->type); + ++map; + } + + irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 2, au1000_ic0r0_dispatch); + irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 3, au1000_ic0r1_dispatch); + irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 4, au1000_ic1r0_dispatch); + irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 5, au1000_ic1r1_dispatch); +} + +static void __init alchemy_gpic_init_irq(const struct alchemy_irqmap *dints) +{ + int i; + void __iomem *bank_base; + + register_syscore_ops(&alchemy_gpic_pmops); + mips_cpu_irq_init(); + + /* disable & ack all possible interrupt sources */ + for (i = 0; i < 4; i++) { + bank_base = AU1300_GPIC_ADDR + (i * 4); + __raw_writel(~0UL, bank_base + AU1300_GPIC_IDIS); + wmb(); + __raw_writel(~0UL, bank_base + AU1300_GPIC_IPEND); + wmb(); + } + + /* register an irq_chip for them, with 2nd highest priority */ + for (i = ALCHEMY_GPIC_INT_BASE; i <= ALCHEMY_GPIC_INT_LAST; i++) { + au1300_set_irq_priority(i, 1); + au1300_gpic_settype(irq_get_irq_data(i), IRQ_TYPE_NONE); + } + + /* setup known on-chip sources */ + while ((i = dints->irq) != -1) { + au1300_gpic_settype(irq_get_irq_data(i), dints->type); + au1300_set_irq_priority(i, dints->prio); + + if (dints->internal) + au1300_pinfunc_to_dev(i - ALCHEMY_GPIC_INT_BASE); + + dints++; + } + + irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 2, alchemy_gpic_dispatch); + irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 3, alchemy_gpic_dispatch); + irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 4, alchemy_gpic_dispatch); + irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 5, alchemy_gpic_dispatch); +} - /* Boards can register additional (GPIO-based) IRQs. - */ - board_init_irq(); +/******************************************************************************/ + +void __init arch_init_irq(void) +{ + switch (alchemy_get_cputype()) { + case ALCHEMY_CPU_AU1000: + au1000_init_irq(au1000_irqmap); + break; + case ALCHEMY_CPU_AU1500: + au1000_init_irq(au1500_irqmap); + break; + case ALCHEMY_CPU_AU1100: + au1000_init_irq(au1100_irqmap); + break; + case ALCHEMY_CPU_AU1550: + au1000_init_irq(au1550_irqmap); + break; + case ALCHEMY_CPU_AU1200: + au1000_init_irq(au1200_irqmap); + break; + case ALCHEMY_CPU_AU1300: + alchemy_gpic_init_irq(au1300_irqmap); + break; + default: + pr_err("unknown Alchemy IRQ core\n"); + break; + } +} - set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3); +asmlinkage void plat_irq_dispatch(void) +{ + unsigned long r = (read_c0_status() & read_c0_cause()) >> 8; + do_IRQ(MIPS_CPU_IRQ_BASE + __ffs(r & 0xff)); } diff --git a/arch/mips/alchemy/common/pci.c b/arch/mips/alchemy/common/pci.c deleted file mode 100644 index 7866cf50cf9..00000000000 --- a/arch/mips/alchemy/common/pci.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * BRIEF MODULE DESCRIPTION - * Alchemy/AMD Au1x00 PCI support. - * - * Copyright 2001-2003, 2007-2008 MontaVista Software Inc. - * Author: MontaVista Software, Inc. <source@mvista.com> - * - * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) - * - * Support for all devices (greater than 16) added by David Gathright. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/pci.h> -#include <linux/kernel.h> -#include <linux/init.h> - -#include <asm/mach-au1x00/au1000.h> - -/* TBD */ -static struct resource pci_io_resource = { - .start = PCI_IO_START, - .end = PCI_IO_END, - .name = "PCI IO space", - .flags = IORESOURCE_IO -}; - -static struct resource pci_mem_resource = { - .start = PCI_MEM_START, - .end = PCI_MEM_END, - .name = "PCI memory space", - .flags = IORESOURCE_MEM -}; - -extern struct pci_ops au1x_pci_ops; - -static struct pci_controller au1x_controller = { - .pci_ops = &au1x_pci_ops, - .io_resource = &pci_io_resource, - .mem_resource = &pci_mem_resource, -}; - -#if defined(CONFIG_SOC_AU1500) || defined(CONFIG_SOC_AU1550) -static unsigned long virt_io_addr; -#endif - -static int __init au1x_pci_setup(void) -{ - extern void au1x_pci_cfg_init(void); - -#if defined(CONFIG_SOC_AU1500) || defined(CONFIG_SOC_AU1550) - virt_io_addr = (unsigned long)ioremap(Au1500_PCI_IO_START, - Au1500_PCI_IO_END - Au1500_PCI_IO_START + 1); - - if (!virt_io_addr) { - printk(KERN_ERR "Unable to ioremap pci space\n"); - return 1; - } - au1x_controller.io_map_base = virt_io_addr; - -#ifdef CONFIG_DMA_NONCOHERENT - { - /* - * Set the NC bit in controller for Au1500 pre-AC silicon - */ - u32 prid = read_c0_prid(); - - if ((prid & 0xFF000000) == 0x01000000 && prid < 0x01030202) { - au_writel((1 << 16) | au_readl(Au1500_PCI_CFG), - Au1500_PCI_CFG); - printk(KERN_INFO "Non-coherent PCI accesses enabled\n"); - } - } -#endif - - set_io_port_base(virt_io_addr); -#endif - - au1x_pci_cfg_init(); - - register_pci_controller(&au1x_controller); - return 0; -} - -arch_initcall(au1x_pci_setup); diff --git a/arch/mips/alchemy/common/platform.c b/arch/mips/alchemy/common/platform.c index 5c76c6448e0..9837a134a6d 100644 --- a/arch/mips/alchemy/common/platform.c +++ b/arch/mips/alchemy/common/platform.c @@ -12,358 +12,430 @@ */ #include <linux/dma-mapping.h> +#include <linux/etherdevice.h> +#include <linux/init.h> #include <linux/platform_device.h> #include <linux/serial_8250.h> -#include <linux/init.h> +#include <linux/slab.h> +#include <linux/usb/ehci_pdriver.h> +#include <linux/usb/ohci_pdriver.h> -#include <asm/mach-au1x00/au1xxx.h> +#include <asm/mach-au1x00/au1000.h> #include <asm/mach-au1x00/au1xxx_dbdma.h> #include <asm/mach-au1x00/au1100_mmc.h> +#include <asm/mach-au1x00/au1xxx_eth.h> -#define PORT(_base, _irq) \ - { \ - .iobase = _base, \ - .membase = (void __iomem *)_base,\ - .mapbase = CPHYSADDR(_base), \ - .irq = _irq, \ - .regshift = 2, \ - .iotype = UPIO_AU, \ - .flags = UPF_SKIP_TEST \ - } +#include <prom.h> -static struct plat_serial8250_port au1x00_uart_data[] = { -#if defined(CONFIG_SERIAL_8250_AU1X00) -#if defined(CONFIG_SOC_AU1000) - PORT(UART0_ADDR, AU1000_UART0_INT), - PORT(UART1_ADDR, AU1000_UART1_INT), - PORT(UART2_ADDR, AU1000_UART2_INT), - PORT(UART3_ADDR, AU1000_UART3_INT), -#elif defined(CONFIG_SOC_AU1500) - PORT(UART0_ADDR, AU1500_UART0_INT), - PORT(UART3_ADDR, AU1500_UART3_INT), -#elif defined(CONFIG_SOC_AU1100) - PORT(UART0_ADDR, AU1100_UART0_INT), - PORT(UART1_ADDR, AU1100_UART1_INT), - PORT(UART3_ADDR, AU1100_UART3_INT), -#elif defined(CONFIG_SOC_AU1550) - PORT(UART0_ADDR, AU1550_UART0_INT), - PORT(UART1_ADDR, AU1550_UART1_INT), - PORT(UART3_ADDR, AU1550_UART3_INT), -#elif defined(CONFIG_SOC_AU1200) - PORT(UART0_ADDR, AU1200_UART0_INT), - PORT(UART1_ADDR, AU1200_UART1_INT), +static void alchemy_8250_pm(struct uart_port *port, unsigned int state, + unsigned int old_state) +{ +#ifdef CONFIG_SERIAL_8250 + switch (state) { + case 0: + alchemy_uart_enable(CPHYSADDR(port->membase)); + serial8250_do_pm(port, state, old_state); + break; + case 3: /* power off */ + serial8250_do_pm(port, state, old_state); + alchemy_uart_disable(CPHYSADDR(port->membase)); + break; + default: + serial8250_do_pm(port, state, old_state); + break; + } #endif -#endif /* CONFIG_SERIAL_8250_AU1X00 */ - { }, +} + +#define PORT(_base, _irq) \ + { \ + .mapbase = _base, \ + .irq = _irq, \ + .regshift = 2, \ + .iotype = UPIO_AU, \ + .flags = UPF_SKIP_TEST | UPF_IOREMAP | \ + UPF_FIXED_TYPE, \ + .type = PORT_16550A, \ + .pm = alchemy_8250_pm, \ + } + +static struct plat_serial8250_port au1x00_uart_data[][4] __initdata = { + [ALCHEMY_CPU_AU1000] = { + PORT(AU1000_UART0_PHYS_ADDR, AU1000_UART0_INT), + PORT(AU1000_UART1_PHYS_ADDR, AU1000_UART1_INT), + PORT(AU1000_UART2_PHYS_ADDR, AU1000_UART2_INT), + PORT(AU1000_UART3_PHYS_ADDR, AU1000_UART3_INT), + }, + [ALCHEMY_CPU_AU1500] = { + PORT(AU1000_UART0_PHYS_ADDR, AU1500_UART0_INT), + PORT(AU1000_UART3_PHYS_ADDR, AU1500_UART3_INT), + }, + [ALCHEMY_CPU_AU1100] = { + PORT(AU1000_UART0_PHYS_ADDR, AU1100_UART0_INT), + PORT(AU1000_UART1_PHYS_ADDR, AU1100_UART1_INT), + PORT(AU1000_UART3_PHYS_ADDR, AU1100_UART3_INT), + }, + [ALCHEMY_CPU_AU1550] = { + PORT(AU1000_UART0_PHYS_ADDR, AU1550_UART0_INT), + PORT(AU1000_UART1_PHYS_ADDR, AU1550_UART1_INT), + PORT(AU1000_UART3_PHYS_ADDR, AU1550_UART3_INT), + }, + [ALCHEMY_CPU_AU1200] = { + PORT(AU1000_UART0_PHYS_ADDR, AU1200_UART0_INT), + PORT(AU1000_UART1_PHYS_ADDR, AU1200_UART1_INT), + }, + [ALCHEMY_CPU_AU1300] = { + PORT(AU1300_UART0_PHYS_ADDR, AU1300_UART0_INT), + PORT(AU1300_UART1_PHYS_ADDR, AU1300_UART1_INT), + PORT(AU1300_UART2_PHYS_ADDR, AU1300_UART2_INT), + PORT(AU1300_UART3_PHYS_ADDR, AU1300_UART3_INT), + }, }; static struct platform_device au1xx0_uart_device = { .name = "serial8250", .id = PLAT8250_DEV_AU1X00, - .dev = { - .platform_data = au1x00_uart_data, - }, }; -/* OHCI (USB full speed host controller) */ -static struct resource au1xxx_usb_ohci_resources[] = { - [0] = { - .start = USB_OHCI_BASE, - .end = USB_OHCI_BASE + USB_OHCI_LEN - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = AU1000_USB_HOST_INT, - .end = AU1000_USB_HOST_INT, - .flags = IORESOURCE_IRQ, - }, -}; +static void __init alchemy_setup_uarts(int ctype) +{ + unsigned int uartclk = get_au1x00_uart_baud_base() * 16; + int s = sizeof(struct plat_serial8250_port); + int c = alchemy_get_uarts(ctype); + struct plat_serial8250_port *ports; + + ports = kzalloc(s * (c + 1), GFP_KERNEL); + if (!ports) { + printk(KERN_INFO "Alchemy: no memory for UART data\n"); + return; + } + memcpy(ports, au1x00_uart_data[ctype], s * c); + au1xx0_uart_device.dev.platform_data = ports; -/* The dmamask must be set for OHCI to work */ -static u64 ohci_dmamask = DMA_32BIT_MASK; + /* Fill up uartclk. */ + for (s = 0; s < c; s++) + ports[s].uartclk = uartclk; + if (platform_device_register(&au1xx0_uart_device)) + printk(KERN_INFO "Alchemy: failed to register UARTs\n"); +} -static struct platform_device au1xxx_usb_ohci_device = { - .name = "au1xxx-ohci", - .id = 0, - .dev = { - .dma_mask = &ohci_dmamask, - .coherent_dma_mask = DMA_32BIT_MASK, - }, - .num_resources = ARRAY_SIZE(au1xxx_usb_ohci_resources), - .resource = au1xxx_usb_ohci_resources, -}; -/*** AU1100 LCD controller ***/ +/* The dmamask must be set for OHCI/EHCI to work */ +static u64 alchemy_ohci_dmamask = DMA_BIT_MASK(32); +static u64 __maybe_unused alchemy_ehci_dmamask = DMA_BIT_MASK(32); -#ifdef CONFIG_FB_AU1100 -static struct resource au1100_lcd_resources[] = { - [0] = { - .start = LCD_PHYS_ADDR, - .end = LCD_PHYS_ADDR + 0x800 - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = AU1100_LCD_INT, - .end = AU1100_LCD_INT, - .flags = IORESOURCE_IRQ, - } -}; +/* Power on callback for the ehci platform driver */ +static int alchemy_ehci_power_on(struct platform_device *pdev) +{ + return alchemy_usb_control(ALCHEMY_USB_EHCI0, 1); +} -static u64 au1100_lcd_dmamask = DMA_32BIT_MASK; +/* Power off/suspend callback for the ehci platform driver */ +static void alchemy_ehci_power_off(struct platform_device *pdev) +{ + alchemy_usb_control(ALCHEMY_USB_EHCI0, 0); +} -static struct platform_device au1100_lcd_device = { - .name = "au1100-lcd", - .id = 0, - .dev = { - .dma_mask = &au1100_lcd_dmamask, - .coherent_dma_mask = DMA_32BIT_MASK, - }, - .num_resources = ARRAY_SIZE(au1100_lcd_resources), - .resource = au1100_lcd_resources, +static struct usb_ehci_pdata alchemy_ehci_pdata = { + .no_io_watchdog = 1, + .power_on = alchemy_ehci_power_on, + .power_off = alchemy_ehci_power_off, + .power_suspend = alchemy_ehci_power_off, }; -#endif -#ifdef CONFIG_SOC_AU1200 -/* EHCI (USB high speed host controller) */ -static struct resource au1xxx_usb_ehci_resources[] = { - [0] = { - .start = USB_EHCI_BASE, - .end = USB_EHCI_BASE + USB_EHCI_LEN - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = AU1000_USB_HOST_INT, - .end = AU1000_USB_HOST_INT, - .flags = IORESOURCE_IRQ, - }, -}; +/* Power on callback for the ohci platform driver */ +static int alchemy_ohci_power_on(struct platform_device *pdev) +{ + int unit; -static u64 ehci_dmamask = DMA_32BIT_MASK; + unit = (pdev->id == 1) ? + ALCHEMY_USB_OHCI1 : ALCHEMY_USB_OHCI0; -static struct platform_device au1xxx_usb_ehci_device = { - .name = "au1xxx-ehci", - .id = 0, - .dev = { - .dma_mask = &ehci_dmamask, - .coherent_dma_mask = DMA_32BIT_MASK, - }, - .num_resources = ARRAY_SIZE(au1xxx_usb_ehci_resources), - .resource = au1xxx_usb_ehci_resources, -}; + return alchemy_usb_control(unit, 1); +} -/* Au1200 UDC (USB gadget controller) */ -static struct resource au1xxx_usb_gdt_resources[] = { - [0] = { - .start = USB_UDC_BASE, - .end = USB_UDC_BASE + USB_UDC_LEN - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = AU1200_USB_INT, - .end = AU1200_USB_INT, - .flags = IORESOURCE_IRQ, - }, -}; +/* Power off/suspend callback for the ohci platform driver */ +static void alchemy_ohci_power_off(struct platform_device *pdev) +{ + int unit; -static u64 udc_dmamask = DMA_32BIT_MASK; + unit = (pdev->id == 1) ? + ALCHEMY_USB_OHCI1 : ALCHEMY_USB_OHCI0; -static struct platform_device au1xxx_usb_gdt_device = { - .name = "au1xxx-udc", - .id = 0, - .dev = { - .dma_mask = &udc_dmamask, - .coherent_dma_mask = DMA_32BIT_MASK, - }, - .num_resources = ARRAY_SIZE(au1xxx_usb_gdt_resources), - .resource = au1xxx_usb_gdt_resources, -}; + alchemy_usb_control(unit, 0); +} -/* Au1200 UOC (USB OTG controller) */ -static struct resource au1xxx_usb_otg_resources[] = { - [0] = { - .start = USB_UOC_BASE, - .end = USB_UOC_BASE + USB_UOC_LEN - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = AU1200_USB_INT, - .end = AU1200_USB_INT, - .flags = IORESOURCE_IRQ, - }, +static struct usb_ohci_pdata alchemy_ohci_pdata = { + .power_on = alchemy_ohci_power_on, + .power_off = alchemy_ohci_power_off, + .power_suspend = alchemy_ohci_power_off, }; -static u64 uoc_dmamask = DMA_32BIT_MASK; +static unsigned long alchemy_ohci_data[][2] __initdata = { + [ALCHEMY_CPU_AU1000] = { AU1000_USB_OHCI_PHYS_ADDR, AU1000_USB_HOST_INT }, + [ALCHEMY_CPU_AU1500] = { AU1000_USB_OHCI_PHYS_ADDR, AU1500_USB_HOST_INT }, + [ALCHEMY_CPU_AU1100] = { AU1000_USB_OHCI_PHYS_ADDR, AU1100_USB_HOST_INT }, + [ALCHEMY_CPU_AU1550] = { AU1550_USB_OHCI_PHYS_ADDR, AU1550_USB_HOST_INT }, + [ALCHEMY_CPU_AU1200] = { AU1200_USB_OHCI_PHYS_ADDR, AU1200_USB_INT }, + [ALCHEMY_CPU_AU1300] = { AU1300_USB_OHCI0_PHYS_ADDR, AU1300_USB_INT }, +}; -static struct platform_device au1xxx_usb_otg_device = { - .name = "au1xxx-uoc", - .id = 0, - .dev = { - .dma_mask = &uoc_dmamask, - .coherent_dma_mask = DMA_32BIT_MASK, - }, - .num_resources = ARRAY_SIZE(au1xxx_usb_otg_resources), - .resource = au1xxx_usb_otg_resources, +static unsigned long alchemy_ehci_data[][2] __initdata = { + [ALCHEMY_CPU_AU1200] = { AU1200_USB_EHCI_PHYS_ADDR, AU1200_USB_INT }, + [ALCHEMY_CPU_AU1300] = { AU1300_USB_EHCI_PHYS_ADDR, AU1300_USB_INT }, }; -static struct resource au1200_lcd_resources[] = { - [0] = { - .start = LCD_PHYS_ADDR, - .end = LCD_PHYS_ADDR + 0x800 - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = AU1200_LCD_INT, - .end = AU1200_LCD_INT, - .flags = IORESOURCE_IRQ, +static int __init _new_usbres(struct resource **r, struct platform_device **d) +{ + *r = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL); + if (!*r) + return -ENOMEM; + *d = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if (!*d) { + kfree(*r); + return -ENOMEM; } -}; -static u64 au1200_lcd_dmamask = DMA_32BIT_MASK; + (*d)->dev.coherent_dma_mask = DMA_BIT_MASK(32); + (*d)->num_resources = 2; + (*d)->resource = *r; -static struct platform_device au1200_lcd_device = { - .name = "au1200-lcd", - .id = 0, - .dev = { - .dma_mask = &au1200_lcd_dmamask, - .coherent_dma_mask = DMA_32BIT_MASK, - }, - .num_resources = ARRAY_SIZE(au1200_lcd_resources), - .resource = au1200_lcd_resources, -}; + return 0; +} -static u64 au1xxx_mmc_dmamask = DMA_32BIT_MASK; +static void __init alchemy_setup_usb(int ctype) +{ + struct resource *res; + struct platform_device *pdev; + + /* setup OHCI0. Every variant has one */ + if (_new_usbres(&res, &pdev)) + return; + + res[0].start = alchemy_ohci_data[ctype][0]; + res[0].end = res[0].start + 0x100 - 1; + res[0].flags = IORESOURCE_MEM; + res[1].start = alchemy_ohci_data[ctype][1]; + res[1].end = res[1].start; + res[1].flags = IORESOURCE_IRQ; + pdev->name = "ohci-platform"; + pdev->id = 0; + pdev->dev.dma_mask = &alchemy_ohci_dmamask; + pdev->dev.platform_data = &alchemy_ohci_pdata; + + if (platform_device_register(pdev)) + printk(KERN_INFO "Alchemy USB: cannot add OHCI0\n"); + + + /* setup EHCI0: Au1200/Au1300 */ + if ((ctype == ALCHEMY_CPU_AU1200) || (ctype == ALCHEMY_CPU_AU1300)) { + if (_new_usbres(&res, &pdev)) + return; + + res[0].start = alchemy_ehci_data[ctype][0]; + res[0].end = res[0].start + 0x100 - 1; + res[0].flags = IORESOURCE_MEM; + res[1].start = alchemy_ehci_data[ctype][1]; + res[1].end = res[1].start; + res[1].flags = IORESOURCE_IRQ; + pdev->name = "ehci-platform"; + pdev->id = 0; + pdev->dev.dma_mask = &alchemy_ehci_dmamask; + pdev->dev.platform_data = &alchemy_ehci_pdata; + + if (platform_device_register(pdev)) + printk(KERN_INFO "Alchemy USB: cannot add EHCI0\n"); + } -extern struct au1xmmc_platform_data au1xmmc_platdata[2]; + /* Au1300: OHCI1 */ + if (ctype == ALCHEMY_CPU_AU1300) { + if (_new_usbres(&res, &pdev)) + return; + + res[0].start = AU1300_USB_OHCI1_PHYS_ADDR; + res[0].end = res[0].start + 0x100 - 1; + res[0].flags = IORESOURCE_MEM; + res[1].start = AU1300_USB_INT; + res[1].end = res[1].start; + res[1].flags = IORESOURCE_IRQ; + pdev->name = "ohci-platform"; + pdev->id = 1; + pdev->dev.dma_mask = &alchemy_ohci_dmamask; + pdev->dev.platform_data = &alchemy_ohci_pdata; + + if (platform_device_register(pdev)) + printk(KERN_INFO "Alchemy USB: cannot add OHCI1\n"); + } +} -static struct resource au1200_mmc0_resources[] = { - [0] = { - .start = SD0_PHYS_ADDR, - .end = SD0_PHYS_ADDR + 0x7ffff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = AU1200_SD_INT, - .end = AU1200_SD_INT, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = DSCR_CMD0_SDMS_TX0, - .end = DSCR_CMD0_SDMS_TX0, - .flags = IORESOURCE_DMA, - }, - [3] = { - .start = DSCR_CMD0_SDMS_RX0, - .end = DSCR_CMD0_SDMS_RX0, - .flags = IORESOURCE_DMA, +/* Macro to help defining the Ethernet MAC resources */ +#define MAC_RES_COUNT 4 /* MAC regs, MAC en, MAC INT, MACDMA regs */ +#define MAC_RES(_base, _enable, _irq, _macdma) \ + { \ + .start = _base, \ + .end = _base + 0xffff, \ + .flags = IORESOURCE_MEM, \ + }, \ + { \ + .start = _enable, \ + .end = _enable + 0x3, \ + .flags = IORESOURCE_MEM, \ + }, \ + { \ + .start = _irq, \ + .end = _irq, \ + .flags = IORESOURCE_IRQ \ + }, \ + { \ + .start = _macdma, \ + .end = _macdma + 0x1ff, \ + .flags = IORESOURCE_MEM, \ } -}; -static struct platform_device au1200_mmc0_device = { - .name = "au1xxx-mmc", - .id = 0, - .dev = { - .dma_mask = &au1xxx_mmc_dmamask, - .coherent_dma_mask = DMA_32BIT_MASK, - .platform_data = &au1xmmc_platdata[0], +static struct resource au1xxx_eth0_resources[][MAC_RES_COUNT] __initdata = { + [ALCHEMY_CPU_AU1000] = { + MAC_RES(AU1000_MAC0_PHYS_ADDR, + AU1000_MACEN_PHYS_ADDR, + AU1000_MAC0_DMA_INT, + AU1000_MACDMA0_PHYS_ADDR) }, - .num_resources = ARRAY_SIZE(au1200_mmc0_resources), - .resource = au1200_mmc0_resources, -}; - -#ifndef CONFIG_MIPS_DB1200 -static struct resource au1200_mmc1_resources[] = { - [0] = { - .start = SD1_PHYS_ADDR, - .end = SD1_PHYS_ADDR + 0x7ffff, - .flags = IORESOURCE_MEM, + [ALCHEMY_CPU_AU1500] = { + MAC_RES(AU1500_MAC0_PHYS_ADDR, + AU1500_MACEN_PHYS_ADDR, + AU1500_MAC0_DMA_INT, + AU1000_MACDMA0_PHYS_ADDR) }, - [1] = { - .start = AU1200_SD_INT, - .end = AU1200_SD_INT, - .flags = IORESOURCE_IRQ, + [ALCHEMY_CPU_AU1100] = { + MAC_RES(AU1000_MAC0_PHYS_ADDR, + AU1000_MACEN_PHYS_ADDR, + AU1100_MAC0_DMA_INT, + AU1000_MACDMA0_PHYS_ADDR) }, - [2] = { - .start = DSCR_CMD0_SDMS_TX1, - .end = DSCR_CMD0_SDMS_TX1, - .flags = IORESOURCE_DMA, + [ALCHEMY_CPU_AU1550] = { + MAC_RES(AU1000_MAC0_PHYS_ADDR, + AU1000_MACEN_PHYS_ADDR, + AU1550_MAC0_DMA_INT, + AU1000_MACDMA0_PHYS_ADDR) }, - [3] = { - .start = DSCR_CMD0_SDMS_RX1, - .end = DSCR_CMD0_SDMS_RX1, - .flags = IORESOURCE_DMA, - } }; -static struct platform_device au1200_mmc1_device = { - .name = "au1xxx-mmc", - .id = 1, - .dev = { - .dma_mask = &au1xxx_mmc_dmamask, - .coherent_dma_mask = DMA_32BIT_MASK, - .platform_data = &au1xmmc_platdata[1], - }, - .num_resources = ARRAY_SIZE(au1200_mmc1_resources), - .resource = au1200_mmc1_resources, +static struct au1000_eth_platform_data au1xxx_eth0_platform_data = { + .phy1_search_mac0 = 1, }; -#endif /* #ifndef CONFIG_MIPS_DB1200 */ -#endif /* #ifdef CONFIG_SOC_AU1200 */ -static struct platform_device au1x00_pcmcia_device = { - .name = "au1x00-pcmcia", - .id = 0, +static struct platform_device au1xxx_eth0_device = { + .name = "au1000-eth", + .id = 0, + .num_resources = MAC_RES_COUNT, + .dev.platform_data = &au1xxx_eth0_platform_data, }; -/* All Alchemy demoboards with I2C have this #define in their headers */ -#ifdef SMBUS_PSC_BASE -static struct resource pbdb_smbus_resources[] = { - { - .start = CPHYSADDR(SMBUS_PSC_BASE), - .end = CPHYSADDR(SMBUS_PSC_BASE + 0xfffff), - .flags = IORESOURCE_MEM, +static struct resource au1xxx_eth1_resources[][MAC_RES_COUNT] __initdata = { + [ALCHEMY_CPU_AU1000] = { + MAC_RES(AU1000_MAC1_PHYS_ADDR, + AU1000_MACEN_PHYS_ADDR + 4, + AU1000_MAC1_DMA_INT, + AU1000_MACDMA1_PHYS_ADDR) + }, + [ALCHEMY_CPU_AU1500] = { + MAC_RES(AU1500_MAC1_PHYS_ADDR, + AU1500_MACEN_PHYS_ADDR + 4, + AU1500_MAC1_DMA_INT, + AU1000_MACDMA1_PHYS_ADDR) + }, + [ALCHEMY_CPU_AU1550] = { + MAC_RES(AU1000_MAC1_PHYS_ADDR, + AU1000_MACEN_PHYS_ADDR + 4, + AU1550_MAC1_DMA_INT, + AU1000_MACDMA1_PHYS_ADDR) }, }; -static struct platform_device pbdb_smbus_device = { - .name = "au1xpsc_smbus", - .id = 0, /* bus number */ - .num_resources = ARRAY_SIZE(pbdb_smbus_resources), - .resource = pbdb_smbus_resources, +static struct au1000_eth_platform_data au1xxx_eth1_platform_data = { + .phy1_search_mac0 = 1, }; -#endif -static struct platform_device *au1xxx_platform_devices[] __initdata = { - &au1xx0_uart_device, - &au1xxx_usb_ohci_device, - &au1x00_pcmcia_device, -#ifdef CONFIG_FB_AU1100 - &au1100_lcd_device, -#endif -#ifdef CONFIG_SOC_AU1200 - &au1xxx_usb_ehci_device, - &au1xxx_usb_gdt_device, - &au1xxx_usb_otg_device, - &au1200_lcd_device, - &au1200_mmc0_device, -#ifndef CONFIG_MIPS_DB1200 - &au1200_mmc1_device, -#endif -#endif -#ifdef SMBUS_PSC_BASE - &pbdb_smbus_device, -#endif +static struct platform_device au1xxx_eth1_device = { + .name = "au1000-eth", + .id = 1, + .num_resources = MAC_RES_COUNT, + .dev.platform_data = &au1xxx_eth1_platform_data, }; +void __init au1xxx_override_eth_cfg(unsigned int port, + struct au1000_eth_platform_data *eth_data) +{ + if (!eth_data || port > 1) + return; + + if (port == 0) + memcpy(&au1xxx_eth0_platform_data, eth_data, + sizeof(struct au1000_eth_platform_data)); + else + memcpy(&au1xxx_eth1_platform_data, eth_data, + sizeof(struct au1000_eth_platform_data)); +} + +static void __init alchemy_setup_macs(int ctype) +{ + int ret, i; + unsigned char ethaddr[6]; + struct resource *macres; + + /* Handle 1st MAC */ + if (alchemy_get_macs(ctype) < 1) + return; + + macres = kmemdup(au1xxx_eth0_resources[ctype], + sizeof(struct resource) * MAC_RES_COUNT, GFP_KERNEL); + if (!macres) { + printk(KERN_INFO "Alchemy: no memory for MAC0 resources\n"); + return; + } + au1xxx_eth0_device.resource = macres; + + i = prom_get_ethernet_addr(ethaddr); + if (!i && !is_valid_ether_addr(au1xxx_eth0_platform_data.mac)) + memcpy(au1xxx_eth0_platform_data.mac, ethaddr, 6); + + ret = platform_device_register(&au1xxx_eth0_device); + if (ret) + printk(KERN_INFO "Alchemy: failed to register MAC0\n"); + + + /* Handle 2nd MAC */ + if (alchemy_get_macs(ctype) < 2) + return; + + macres = kmemdup(au1xxx_eth1_resources[ctype], + sizeof(struct resource) * MAC_RES_COUNT, GFP_KERNEL); + if (!macres) { + printk(KERN_INFO "Alchemy: no memory for MAC1 resources\n"); + return; + } + au1xxx_eth1_device.resource = macres; + + ethaddr[5] += 1; /* next addr for 2nd MAC */ + if (!i && !is_valid_ether_addr(au1xxx_eth1_platform_data.mac)) + memcpy(au1xxx_eth1_platform_data.mac, ethaddr, 6); + + /* Register second MAC if enabled in pinfunc */ + if (!(au_readl(SYS_PINFUNC) & (u32)SYS_PF_NI2)) { + ret = platform_device_register(&au1xxx_eth1_device); + if (ret) + printk(KERN_INFO "Alchemy: failed to register MAC1\n"); + } +} + static int __init au1xxx_platform_init(void) { - unsigned int uartclk = get_au1x00_uart_baud_base() * 16; - int i; + int ctype = alchemy_get_cputype(); - /* Fill up uartclk. */ - for (i = 0; au1x00_uart_data[i].flags; i++) - au1x00_uart_data[i].uartclk = uartclk; + alchemy_setup_uarts(ctype); + alchemy_setup_macs(ctype); + alchemy_setup_usb(ctype); - return platform_add_devices(au1xxx_platform_devices, - ARRAY_SIZE(au1xxx_platform_devices)); + return 0; } arch_initcall(au1xxx_platform_init); diff --git a/arch/mips/alchemy/common/power.c b/arch/mips/alchemy/common/power.c index 6ab7b42aa1b..bdb28dee8fd 100644 --- a/arch/mips/alchemy/common/power.c +++ b/arch/mips/alchemy/common/power.c @@ -29,18 +29,12 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/init.h> #include <linux/pm.h> #include <linux/sysctl.h> #include <linux/jiffies.h> #include <asm/uaccess.h> #include <asm/mach-au1x00/au1000.h> -#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) -#include <asm/mach-au1x00/au1xxx_dbdma.h> -#endif - -#ifdef CONFIG_PM /* * We need to save/restore a bunch of core registers that are @@ -52,12 +46,6 @@ * We only have to save/restore registers that aren't otherwise * done as part of a driver pm_* function. */ -static unsigned int sleep_uart0_inten; -static unsigned int sleep_uart0_fifoctl; -static unsigned int sleep_uart0_linectl; -static unsigned int sleep_uart0_clkdiv; -static unsigned int sleep_uart0_enable; -static unsigned int sleep_usb[2]; static unsigned int sleep_sys_clocks[5]; static unsigned int sleep_sys_pinfunc; static unsigned int sleep_static_memctlr[4][3]; @@ -65,50 +53,6 @@ static unsigned int sleep_static_memctlr[4][3]; static void save_core_regs(void) { - extern void save_au1xxx_intctl(void); - extern void pm_eth0_shutdown(void); - - /* - * Do the serial ports.....these really should be a pm_* - * registered function by the driver......but of course the - * standard serial driver doesn't understand our Au1xxx - * unique registers. - */ - sleep_uart0_inten = au_readl(UART0_ADDR + UART_IER); - sleep_uart0_fifoctl = au_readl(UART0_ADDR + UART_FCR); - sleep_uart0_linectl = au_readl(UART0_ADDR + UART_LCR); - sleep_uart0_clkdiv = au_readl(UART0_ADDR + UART_CLK); - sleep_uart0_enable = au_readl(UART0_ADDR + UART_MOD_CNTRL); - au_sync(); - -#ifndef CONFIG_SOC_AU1200 - /* Shutdown USB host/device. */ - sleep_usb[0] = au_readl(USB_HOST_CONFIG); - - /* There appears to be some undocumented reset register.... */ - au_writel(0, 0xb0100004); - au_sync(); - au_writel(0, USB_HOST_CONFIG); - au_sync(); - - sleep_usb[1] = au_readl(USBD_ENABLE); - au_writel(0, USBD_ENABLE); - au_sync(); - -#else /* AU1200 */ - - /* enable access to OTG mmio so we can save OTG CAP/MUX. - * FIXME: write an OTG driver and move this stuff there! - */ - au_writel(au_readl(USB_MSR_BASE + 4) | (1 << 6), USB_MSR_BASE + 4); - au_sync(); - sleep_usb[0] = au_readl(0xb4020020); /* OTG_CAP */ - sleep_usb[1] = au_readl(0xb4020024); /* OTG_MUX */ -#endif - - /* Save interrupt controller state. */ - save_au1xxx_intctl(); - /* Clocks and PLLs. */ sleep_sys_clocks[0] = au_readl(SYS_FREQCTRL0); sleep_sys_clocks[1] = au_readl(SYS_FREQCTRL1); @@ -132,10 +76,6 @@ static void save_core_regs(void) sleep_static_memctlr[3][0] = au_readl(MEM_STCFG3); sleep_static_memctlr[3][1] = au_readl(MEM_STTIME3); sleep_static_memctlr[3][2] = au_readl(MEM_STADDR3); - -#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) - au1xxx_dbdma_suspend(); -#endif } static void restore_core_regs(void) @@ -156,22 +96,6 @@ static void restore_core_regs(void) au_writel(sleep_sys_pinfunc, SYS_PINFUNC); au_sync(); -#ifndef CONFIG_SOC_AU1200 - au_writel(sleep_usb[0], USB_HOST_CONFIG); - au_writel(sleep_usb[1], USBD_ENABLE); - au_sync(); -#else - /* enable accces to OTG memory */ - au_writel(au_readl(USB_MSR_BASE + 4) | (1 << 6), USB_MSR_BASE + 4); - au_sync(); - - /* restore OTG caps and port mux. */ - au_writel(sleep_usb[0], 0xb4020020 + 0); /* OTG_CAP */ - au_sync(); - au_writel(sleep_usb[1], 0xb4020020 + 4); /* OTG_MUX */ - au_sync(); -#endif - /* Restore the static memory controller configuration. */ au_writel(sleep_static_memctlr[0][0], MEM_STCFG0); au_writel(sleep_static_memctlr[0][1], MEM_STTIME0); @@ -185,33 +109,26 @@ static void restore_core_regs(void) au_writel(sleep_static_memctlr[3][0], MEM_STCFG3); au_writel(sleep_static_memctlr[3][1], MEM_STTIME3); au_writel(sleep_static_memctlr[3][2], MEM_STADDR3); - - /* - * Enable the UART if it was enabled before sleep. - * I guess I should define module control bits........ - */ - if (sleep_uart0_enable & 0x02) { - au_writel(0, UART0_ADDR + UART_MOD_CNTRL); au_sync(); - au_writel(1, UART0_ADDR + UART_MOD_CNTRL); au_sync(); - au_writel(3, UART0_ADDR + UART_MOD_CNTRL); au_sync(); - au_writel(sleep_uart0_inten, UART0_ADDR + UART_IER); au_sync(); - au_writel(sleep_uart0_fifoctl, UART0_ADDR + UART_FCR); au_sync(); - au_writel(sleep_uart0_linectl, UART0_ADDR + UART_LCR); au_sync(); - au_writel(sleep_uart0_clkdiv, UART0_ADDR + UART_CLK); au_sync(); - } - - restore_au1xxx_intctl(); - -#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) - au1xxx_dbdma_resume(); -#endif } void au_sleep(void) { save_core_regs(); - au1xxx_save_and_sleep(); + + switch (alchemy_get_cputype()) { + case ALCHEMY_CPU_AU1000: + case ALCHEMY_CPU_AU1500: + case ALCHEMY_CPU_AU1100: + alchemy_sleep_au1000(); + break; + case ALCHEMY_CPU_AU1550: + case ALCHEMY_CPU_AU1200: + alchemy_sleep_au1550(); + break; + case ALCHEMY_CPU_AU1300: + alchemy_sleep_au1300(); + break; + } + restore_core_regs(); } - -#endif /* CONFIG_PM */ diff --git a/arch/mips/alchemy/common/prom.c b/arch/mips/alchemy/common/prom.c index 18b310b475c..53402105962 100644 --- a/arch/mips/alchemy/common/prom.c +++ b/arch/mips/alchemy/common/prom.c @@ -43,29 +43,15 @@ int prom_argc; char **prom_argv; char **prom_envp; -char * __init_or_module prom_getcmdline(void) +void __init prom_init_cmdline(void) { - return &(arcs_cmdline[0]); -} - -void prom_init_cmdline(void) -{ - char *cp; - int actr; - - actr = 1; /* Always ignore argv[0] */ + int i; - cp = &(arcs_cmdline[0]); - while (actr < prom_argc) { - strcpy(cp, prom_argv[actr]); - cp += strlen(prom_argv[actr]); - *cp++ = ' '; - actr++; + for (i = 1; i < prom_argc; i++) { + strlcat(arcs_cmdline, prom_argv[i], COMMAND_LINE_SIZE); + if (i < (prom_argc - 1)) + strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE); } - if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */ - --cp; - if (prom_argc > 1) - *cp = '\0'; } char *prom_getenv(char *envname) @@ -118,17 +104,15 @@ static inline void str2eaddr(unsigned char *ea, unsigned char *str) } } -int prom_get_ethernet_addr(char *ethernet_addr) +int __init prom_get_ethernet_addr(char *ethernet_addr) { char *ethaddr_str; - char *argptr; /* Check the environment variables first */ ethaddr_str = prom_getenv("ethaddr"); if (!ethaddr_str) { /* Check command line */ - argptr = prom_getcmdline(); - ethaddr_str = strstr(argptr, "ethaddr="); + ethaddr_str = strstr(arcs_cmdline, "ethaddr="); if (!ethaddr_str) return -1; @@ -139,7 +123,6 @@ int prom_get_ethernet_addr(char *ethernet_addr) return 0; } -EXPORT_SYMBOL(prom_get_ethernet_addr); void __init prom_free_prom_memory(void) { diff --git a/arch/mips/alchemy/common/puts.c b/arch/mips/alchemy/common/puts.c deleted file mode 100644 index 55bbe24d45b..00000000000 --- a/arch/mips/alchemy/common/puts.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * BRIEF MODULE DESCRIPTION - * Low level UART routines to directly access Alchemy UART. - * - * Copyright 2001, 2008 MontaVista Software Inc. - * Author: MontaVista Software, Inc. <source@mvista.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <asm/mach-au1x00/au1000.h> - -#define SERIAL_BASE UART_BASE -#define SER_CMD 0x7 -#define SER_DATA 0x1 -#define TX_BUSY 0x20 - -#define TIMEOUT 0xffffff -#define SLOW_DOWN - -static volatile unsigned long * const com1 = (unsigned long *)SERIAL_BASE; - -#ifdef SLOW_DOWN -static inline void slow_down(void) -{ - int k; - - for (k = 0; k < 10000; k++); -} -#else -#define slow_down() -#endif - -void -prom_putchar(const unsigned char c) -{ - unsigned char ch; - int i = 0; - - do { - ch = com1[SER_CMD]; - slow_down(); - i++; - if (i > TIMEOUT) - break; - } while (0 == (ch & TX_BUSY)); - - com1[SER_DATA] = c; -} diff --git a/arch/mips/alchemy/common/reset.c b/arch/mips/alchemy/common/reset.c deleted file mode 100644 index 0191c936cb5..00000000000 --- a/arch/mips/alchemy/common/reset.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * - * BRIEF MODULE DESCRIPTION - * Au1xx0 reset routines. - * - * Copyright 2001, 2006, 2008 MontaVista Software Inc. - * Author: MontaVista Software, Inc. <source@mvista.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <asm/cacheflush.h> - -#include <asm/mach-au1x00/au1000.h> - -void au1000_restart(char *command) -{ - /* Set all integrated peripherals to disabled states */ - extern void board_reset(void); - u32 prid = read_c0_prid(); - - printk(KERN_NOTICE "\n** Resetting Integrated Peripherals\n"); - - switch (prid & 0xFF000000) { - case 0x00000000: /* Au1000 */ - au_writel(0x02, 0xb0000010); /* ac97_enable */ - au_writel(0x08, 0xb017fffc); /* usbh_enable - early errata */ - asm("sync"); - au_writel(0x00, 0xb017fffc); /* usbh_enable */ - au_writel(0x00, 0xb0200058); /* usbd_enable */ - au_writel(0x00, 0xb0300040); /* ir_enable */ - au_writel(0x00, 0xb4004104); /* mac dma */ - au_writel(0x00, 0xb4004114); /* mac dma */ - au_writel(0x00, 0xb4004124); /* mac dma */ - au_writel(0x00, 0xb4004134); /* mac dma */ - au_writel(0x00, 0xb0520000); /* macen0 */ - au_writel(0x00, 0xb0520004); /* macen1 */ - au_writel(0x00, 0xb1000008); /* i2s_enable */ - au_writel(0x00, 0xb1100100); /* uart0_enable */ - au_writel(0x00, 0xb1200100); /* uart1_enable */ - au_writel(0x00, 0xb1300100); /* uart2_enable */ - au_writel(0x00, 0xb1400100); /* uart3_enable */ - au_writel(0x02, 0xb1600100); /* ssi0_enable */ - au_writel(0x02, 0xb1680100); /* ssi1_enable */ - au_writel(0x00, 0xb1900020); /* sys_freqctrl0 */ - au_writel(0x00, 0xb1900024); /* sys_freqctrl1 */ - au_writel(0x00, 0xb1900028); /* sys_clksrc */ - au_writel(0x10, 0xb1900060); /* sys_cpupll */ - au_writel(0x00, 0xb1900064); /* sys_auxpll */ - au_writel(0x00, 0xb1900100); /* sys_pininputen */ - break; - case 0x01000000: /* Au1500 */ - au_writel(0x02, 0xb0000010); /* ac97_enable */ - au_writel(0x08, 0xb017fffc); /* usbh_enable - early errata */ - asm("sync"); - au_writel(0x00, 0xb017fffc); /* usbh_enable */ - au_writel(0x00, 0xb0200058); /* usbd_enable */ - au_writel(0x00, 0xb4004104); /* mac dma */ - au_writel(0x00, 0xb4004114); /* mac dma */ - au_writel(0x00, 0xb4004124); /* mac dma */ - au_writel(0x00, 0xb4004134); /* mac dma */ - au_writel(0x00, 0xb1520000); /* macen0 */ - au_writel(0x00, 0xb1520004); /* macen1 */ - au_writel(0x00, 0xb1100100); /* uart0_enable */ - au_writel(0x00, 0xb1400100); /* uart3_enable */ - au_writel(0x00, 0xb1900020); /* sys_freqctrl0 */ - au_writel(0x00, 0xb1900024); /* sys_freqctrl1 */ - au_writel(0x00, 0xb1900028); /* sys_clksrc */ - au_writel(0x10, 0xb1900060); /* sys_cpupll */ - au_writel(0x00, 0xb1900064); /* sys_auxpll */ - au_writel(0x00, 0xb1900100); /* sys_pininputen */ - break; - case 0x02000000: /* Au1100 */ - au_writel(0x02, 0xb0000010); /* ac97_enable */ - au_writel(0x08, 0xb017fffc); /* usbh_enable - early errata */ - asm("sync"); - au_writel(0x00, 0xb017fffc); /* usbh_enable */ - au_writel(0x00, 0xb0200058); /* usbd_enable */ - au_writel(0x00, 0xb0300040); /* ir_enable */ - au_writel(0x00, 0xb4004104); /* mac dma */ - au_writel(0x00, 0xb4004114); /* mac dma */ - au_writel(0x00, 0xb4004124); /* mac dma */ - au_writel(0x00, 0xb4004134); /* mac dma */ - au_writel(0x00, 0xb0520000); /* macen0 */ - au_writel(0x00, 0xb1000008); /* i2s_enable */ - au_writel(0x00, 0xb1100100); /* uart0_enable */ - au_writel(0x00, 0xb1200100); /* uart1_enable */ - au_writel(0x00, 0xb1400100); /* uart3_enable */ - au_writel(0x02, 0xb1600100); /* ssi0_enable */ - au_writel(0x02, 0xb1680100); /* ssi1_enable */ - au_writel(0x00, 0xb1900020); /* sys_freqctrl0 */ - au_writel(0x00, 0xb1900024); /* sys_freqctrl1 */ - au_writel(0x00, 0xb1900028); /* sys_clksrc */ - au_writel(0x10, 0xb1900060); /* sys_cpupll */ - au_writel(0x00, 0xb1900064); /* sys_auxpll */ - au_writel(0x00, 0xb1900100); /* sys_pininputen */ - break; - case 0x03000000: /* Au1550 */ - au_writel(0x00, 0xb1a00004); /* psc 0 */ - au_writel(0x00, 0xb1b00004); /* psc 1 */ - au_writel(0x00, 0xb0a00004); /* psc 2 */ - au_writel(0x00, 0xb0b00004); /* psc 3 */ - au_writel(0x00, 0xb017fffc); /* usbh_enable */ - au_writel(0x00, 0xb0200058); /* usbd_enable */ - au_writel(0x00, 0xb4004104); /* mac dma */ - au_writel(0x00, 0xb4004114); /* mac dma */ - au_writel(0x00, 0xb4004124); /* mac dma */ - au_writel(0x00, 0xb4004134); /* mac dma */ - au_writel(0x00, 0xb1520000); /* macen0 */ - au_writel(0x00, 0xb1520004); /* macen1 */ - au_writel(0x00, 0xb1100100); /* uart0_enable */ - au_writel(0x00, 0xb1200100); /* uart1_enable */ - au_writel(0x00, 0xb1400100); /* uart3_enable */ - au_writel(0x00, 0xb1900020); /* sys_freqctrl0 */ - au_writel(0x00, 0xb1900024); /* sys_freqctrl1 */ - au_writel(0x00, 0xb1900028); /* sys_clksrc */ - au_writel(0x10, 0xb1900060); /* sys_cpupll */ - au_writel(0x00, 0xb1900064); /* sys_auxpll */ - au_writel(0x00, 0xb1900100); /* sys_pininputen */ - break; - } - - set_c0_status(ST0_BEV | ST0_ERL); - change_c0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); - flush_cache_all(); - write_c0_wired(0); - - /* Give board a chance to do a hardware reset */ - board_reset(); - - /* Jump to the beggining in case board_reset() is empty */ - __asm__ __volatile__("jr\t%0"::"r"(0xbfc00000)); -} - -void au1000_halt(void) -{ -#if defined(CONFIG_MIPS_PB1550) || defined(CONFIG_MIPS_DB1550) - /* Power off system */ - printk(KERN_NOTICE "\n** Powering off...\n"); - au_writew(au_readw(0xAF00001C) | (3 << 14), 0xAF00001C); - au_sync(); - while (1); /* should not get here */ -#else - printk(KERN_NOTICE "\n** You can safely turn off the power\n"); -#ifdef CONFIG_MIPS_MIRAGE - au_writel((1 << 26) | (1 << 10), GPIO2_OUTPUT); -#endif -#ifdef CONFIG_MIPS_DB1200 - au_writew(au_readw(0xB980001C) | (1 << 14), 0xB980001C); -#endif -#ifdef CONFIG_PM - au_sleep(); - - /* Should not get here */ - printk(KERN_ERR "Unable to put CPU in sleep mode\n"); - while (1); -#else - while (1) - __asm__(".set\tmips3\n\t" - "wait\n\t" - ".set\tmips0"); -#endif -#endif /* defined(CONFIG_MIPS_PB1550) || defined(CONFIG_MIPS_DB1550) */ -} - -void au1000_power_off(void) -{ - au1000_halt(); -} diff --git a/arch/mips/alchemy/common/setup.c b/arch/mips/alchemy/common/setup.c index 3f036b3d400..8267e3c9772 100644 --- a/arch/mips/alchemy/common/setup.c +++ b/arch/mips/alchemy/common/setup.c @@ -27,19 +27,16 @@ #include <linux/init.h> #include <linux/ioport.h> +#include <linux/jiffies.h> #include <linux/module.h> -#include <linux/pm.h> +#include <asm/dma-coherence.h> #include <asm/mipsregs.h> -#include <asm/reboot.h> #include <asm/time.h> #include <au1000.h> extern void __init board_setup(void); -extern void au1000_restart(char *); -extern void au1000_halt(void); -extern void au1000_power_off(void); extern void set_cpuspec(void); void __init plat_mem_setup(void) @@ -53,11 +50,8 @@ void __init plat_mem_setup(void) printk(KERN_INFO "(PRId %08x) @ %lu.%02lu MHz\n", read_c0_prid(), est_freq / 1000000, ((est_freq % 1000000) * 100) / 1000000); - _machine_restart = au1000_restart; - _machine_halt = au1000_halt; - pm_power_off = au1000_power_off; - - board_setup(); /* board specific setup */ + /* this is faster than wasting cycles trying to approximate it */ + preset_lpj = (est_freq >> 1) / HZ; if (au1xxx_cpu_needs_config_od()) /* Various early Au1xx0 errata corrected by this */ @@ -66,6 +60,23 @@ void __init plat_mem_setup(void) /* Clear to obtain best system bus performance */ clear_c0_config(1 << 19); /* Clear Config[OD] */ + hw_coherentio = 0; + coherentio = 1; + switch (alchemy_get_cputype()) { + case ALCHEMY_CPU_AU1000: + case ALCHEMY_CPU_AU1500: + case ALCHEMY_CPU_AU1100: + coherentio = 0; + break; + case ALCHEMY_CPU_AU1200: + /* Au1200 AB USB does not support coherent memory */ + if (0 == (read_c0_prid() & PRID_REV_MASK)) + coherentio = 0; + break; + } + + board_setup(); /* board specific setup */ + /* IO/MEM resources. */ set_io_port_base(0); ioport_resource.start = IOPORT_RESOURCE_START; @@ -74,37 +85,20 @@ void __init plat_mem_setup(void) iomem_resource.end = IOMEM_RESOURCE_END; } -#if defined(CONFIG_64BIT_PHYS_ADDR) +#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_PCI) /* This routine should be valid for all Au1x based boards */ phys_t __fixup_bigphys_addr(phys_t phys_addr, phys_t size) { + unsigned long start = ALCHEMY_PCI_MEMWIN_START; + unsigned long end = ALCHEMY_PCI_MEMWIN_END; + /* Don't fixup 36-bit addresses */ if ((phys_addr >> 32) != 0) return phys_addr; -#ifdef CONFIG_PCI - { - u32 start = (u32)Au1500_PCI_MEM_START; - u32 end = (u32)Au1500_PCI_MEM_END; - - /* Check for PCI memory window */ - if (phys_addr >= start && (phys_addr + size - 1) <= end) - return (phys_t) - ((phys_addr - start) + Au1500_PCI_MEM_START); - } -#endif - - /* - * All Au1xx0 SOCs have a PCMCIA controller. - * We setup our 32-bit pseudo addresses to be equal to the - * 36-bit addr >> 4, to make it easier to check the address - * and fix it. - * The PCMCIA socket 0 physical attribute address is 0xF 4000 0000. - * The pseudo address we use is 0xF400 0000. Any address over - * 0xF400 0000 is a PCMCIA pseudo address. - */ - if ((phys_addr >= 0xF4000000) && (phys_addr < 0xFFFFFFFF)) - return (phys_t)(phys_addr << 4); + /* Check for PCI memory window */ + if (phys_addr >= start && (phys_addr + size - 1) <= end) + return (phys_t)(AU1500_PCI_MEM_PHYS_ADDR + phys_addr); /* default nop */ return phys_addr; diff --git a/arch/mips/alchemy/common/sleeper.S b/arch/mips/alchemy/common/sleeper.S index 4f4b16741d1..c73d81270b4 100644 --- a/arch/mips/alchemy/common/sleeper.S +++ b/arch/mips/alchemy/common/sleeper.S @@ -22,10 +22,9 @@ .set noat .align 5 -/* Save all of the processor general registers and go to sleep. - * A wakeup condition will get us back here to restore the registers. - */ -LEAF(au1xxx_save_and_sleep) + +/* preparatory stuff */ +.macro SETUP_SLEEP subu sp, PT_SIZE sw $1, PT_R1(sp) sw $2, PT_R2(sp) @@ -69,40 +68,75 @@ LEAF(au1xxx_save_and_sleep) */ lui t3, 0xb190 /* sys_xxx */ sw sp, 0x0018(t3) - la k0, 3f /* resume path */ + la k0, alchemy_sleep_wakeup /* resume path */ sw k0, 0x001c(t3) +.endm - /* Put SDRAM into self refresh: Preload instructions into cache, - * issue a precharge, auto/self refresh, then sleep commands to it. - */ +.macro DO_SLEEP + /* put power supply and processor to sleep */ + sw zero, 0x0078(t3) /* sys_slppwr */ + sync + sw zero, 0x007c(t3) /* sys_sleep */ + sync + nop + nop + nop + nop + nop + nop + nop + nop +.endm + +/* sleep code for Au1000/Au1100/Au1500 memory controller type */ +LEAF(alchemy_sleep_au1000) + + SETUP_SLEEP + + /* cache following instructions, as memory gets put to sleep */ la t0, 1f - .set mips3 + .set arch=r4000 cache 0x14, 0(t0) cache 0x14, 32(t0) cache 0x14, 64(t0) cache 0x14, 96(t0) .set mips0 -1: lui a0, 0xb400 /* mem_xxx */ -#if defined(CONFIG_SOC_AU1000) || defined(CONFIG_SOC_AU1100) || \ - defined(CONFIG_SOC_AU1500) - sw zero, 0x001c(a0) /* Precharge */ +1: lui a0, 0xb400 /* mem_xxx */ + sw zero, 0x001c(a0) /* Precharge */ sync sw zero, 0x0020(a0) /* Auto Refresh */ sync - sw zero, 0x0030(a0) /* Sleep */ + sw zero, 0x0030(a0) /* Sleep */ sync -#endif -#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) - sw zero, 0x08c0(a0) /* Precharge */ + DO_SLEEP + +END(alchemy_sleep_au1000) + +/* sleep code for Au1550/Au1200 memory controller type */ +LEAF(alchemy_sleep_au1550) + + SETUP_SLEEP + + /* cache following instructions, as memory gets put to sleep */ + la t0, 1f + .set arch=r4000 + cache 0x14, 0(t0) + cache 0x14, 32(t0) + cache 0x14, 64(t0) + cache 0x14, 96(t0) + .set mips0 + +1: lui a0, 0xb400 /* mem_xxx */ + sw zero, 0x08c0(a0) /* Precharge */ sync sw zero, 0x08d0(a0) /* Self Refresh */ sync /* wait for sdram to enter self-refresh mode */ - lui t0, 0x0100 -2: lw t1, 0x0850(a0) /* mem_sdstat */ + lui t0, 0x0100 +2: lw t1, 0x0850(a0) /* mem_sdstat */ and t2, t1, t0 beq t2, zero, 2b nop @@ -110,30 +144,94 @@ LEAF(au1xxx_save_and_sleep) /* disable SDRAM clocks */ lui t0, 0xcfff ori t0, t0, 0xffff - lw t1, 0x0840(a0) /* mem_sdconfiga */ - and t1, t0, t1 /* clear CE[1:0] */ - sw t1, 0x0840(a0) /* mem_sdconfiga */ + lw t1, 0x0840(a0) /* mem_sdconfiga */ + and t1, t0, t1 /* clear CE[1:0] */ + sw t1, 0x0840(a0) /* mem_sdconfiga */ sync -#endif - /* put power supply and processor to sleep */ - sw zero, 0x0078(t3) /* sys_slppwr */ + DO_SLEEP + +END(alchemy_sleep_au1550) + +/* sleepcode for Au1300 memory controller type */ +LEAF(alchemy_sleep_au1300) + + SETUP_SLEEP + + /* cache following instructions, as memory gets put to sleep */ + la t0, 2f + la t1, 4f + subu t2, t1, t0 + + .set arch=r4000 + +1: cache 0x14, 0(t0) + subu t2, t2, 32 + bgez t2, 1b + addu t0, t0, 32 + + .set mips0 + +2: lui a0, 0xb400 /* mem_xxx */ + + /* disable all ports in mem_sdportcfga */ + sw zero, 0x868(a0) /* mem_sdportcfga */ sync - sw zero, 0x007c(t3) /* sys_sleep */ + + /* disable ODT */ + li t0, 0x03010000 + sw t0, 0x08d8(a0) /* mem_sdcmd0 */ + sw t0, 0x08dc(a0) /* mem_sdcmd1 */ sync - nop - nop - nop - nop - nop - nop - nop - nop + + /* precharge */ + li t0, 0x23000400 + sw t0, 0x08dc(a0) /* mem_sdcmd1 */ + sw t0, 0x08d8(a0) /* mem_sdcmd0 */ + sync + + /* auto refresh */ + sw zero, 0x08c8(a0) /* mem_sdautoref */ + sync + + /* block access to the DDR */ + lw t0, 0x0848(a0) /* mem_sdconfigb */ + li t1, (1 << 7 | 0x3F) + or t0, t0, t1 + sw t0, 0x0848(a0) /* mem_sdconfigb */ + sync + + /* issue the Self Refresh command */ + li t0, 0x10000000 + sw t0, 0x08dc(a0) /* mem_sdcmd1 */ + sw t0, 0x08d8(a0) /* mem_sdcmd0 */ + sync + + /* wait for sdram to enter self-refresh mode */ + lui t0, 0x0300 +3: lw t1, 0x0850(a0) /* mem_sdstat */ + and t2, t1, t0 + bne t2, t0, 3b + nop + + /* disable SDRAM clocks */ + li t0, ~(3<<28) + lw t1, 0x0840(a0) /* mem_sdconfiga */ + and t1, t1, t0 /* clear CE[1:0] */ + sw t1, 0x0840(a0) /* mem_sdconfiga */ + sync + + DO_SLEEP +4: + +END(alchemy_sleep_au1300) + /* This is where we return upon wakeup. * Reload all of the registers and return. */ -3: lw k0, 0x20(sp) +LEAF(alchemy_sleep_wakeup) + lw k0, 0x20(sp) mtc0 k0, CP0_STATUS lw k0, 0x1c(sp) mtc0 k0, CP0_CONTEXT @@ -169,4 +267,4 @@ LEAF(au1xxx_save_and_sleep) lw $31, PT_R31(sp) jr ra addiu sp, PT_SIZE -END(au1xxx_save_and_sleep) +END(alchemy_sleep_wakeup) diff --git a/arch/mips/alchemy/common/time.c b/arch/mips/alchemy/common/time.c index f58d4ffb894..93fa586d52e 100644 --- a/arch/mips/alchemy/common/time.c +++ b/arch/mips/alchemy/common/time.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Manuel Lauss <mano@roarinelk.homelinux.net> + * Copyright (C) 2008-2009 Manuel Lauss <manuel.lauss@gmail.com> * * Previous incarnations were: * Copyright (C) 2001, 2006, 2008 MontaVista Software, <source@mvista.com> @@ -36,15 +36,15 @@ #include <linux/interrupt.h> #include <linux/spinlock.h> +#include <asm/idle.h> +#include <asm/processor.h> #include <asm/time.h> #include <asm/mach-au1x00/au1000.h> /* 32kHz clock enabled and detected */ #define CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S) -extern int allow_au1k_wait; /* default off for CP0 Counter */ - -static cycle_t au1x_counter1_read(void) +static cycle_t au1x_counter1_read(struct clocksource *cs) { return au_readl(SYS_RTCREAD); } @@ -54,7 +54,7 @@ static struct clocksource au1x_counter1_clocksource = { .read = au1x_counter1_read, .mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, - .rating = 100, + .rating = 1500, }; static int au1x_rtcmatch2_set_next_event(unsigned long delta, @@ -85,25 +85,26 @@ static irqreturn_t au1x_rtcmatch2_irq(int irq, void *dev_id) static struct clock_event_device au1x_rtcmatch2_clockdev = { .name = "rtcmatch2", .features = CLOCK_EVT_FEAT_ONESHOT, - .rating = 100, - .irq = AU1000_RTC_MATCH2_INT, - .set_next_event = au1x_rtcmatch2_set_next_event, + .rating = 1500, + .set_next_event = au1x_rtcmatch2_set_next_event, .set_mode = au1x_rtcmatch2_set_mode, - .cpumask = CPU_MASK_ALL_PTR, + .cpumask = cpu_all_mask, }; static struct irqaction au1x_rtcmatch2_irqaction = { .handler = au1x_rtcmatch2_irq, - .flags = IRQF_DISABLED | IRQF_TIMER, + .flags = IRQF_TIMER, .name = "timer", .dev_id = &au1x_rtcmatch2_clockdev, }; -void __init plat_time_init(void) +static int __init alchemy_time_init(unsigned int m2int) { struct clock_event_device *cd = &au1x_rtcmatch2_clockdev; unsigned long t; + au1x_rtcmatch2_clockdev.irq = m2int; + /* Check if firmware (YAMON, ...) has enabled 32kHz and clock * has been detected. If so install the rtcmatch2 clocksource, * otherwise don't bother. Note that both bits being set is by @@ -141,25 +142,38 @@ void __init plat_time_init(void) goto cntr_err; /* register counter1 clocksource and event device */ - clocksource_set_clock(&au1x_counter1_clocksource, 32768); - clocksource_register(&au1x_counter1_clocksource); + clocksource_register_hz(&au1x_counter1_clocksource, 32768); cd->shift = 32; cd->mult = div_sc(32768, NSEC_PER_SEC, cd->shift); cd->max_delta_ns = clockevent_delta2ns(0xffffffff, cd); - cd->min_delta_ns = clockevent_delta2ns(8, cd); /* ~0.25ms */ + cd->min_delta_ns = clockevent_delta2ns(9, cd); /* ~0.28ms */ clockevents_register_device(cd); - setup_irq(AU1000_RTC_MATCH2_INT, &au1x_rtcmatch2_irqaction); + setup_irq(m2int, &au1x_rtcmatch2_irqaction); printk(KERN_INFO "Alchemy clocksource installed\n"); - /* can now use 'wait' */ - allow_au1k_wait = 1; - return; + return 0; cntr_err: - /* counters unusable, use C0 counter */ - r4k_clockevent_init(); - init_r4k_clocksource(); - allow_au1k_wait = 0; + return -1; +} + +static int alchemy_m2inttab[] __initdata = { + AU1000_RTC_MATCH2_INT, + AU1500_RTC_MATCH2_INT, + AU1100_RTC_MATCH2_INT, + AU1550_RTC_MATCH2_INT, + AU1200_RTC_MATCH2_INT, + AU1300_RTC_MATCH2_INT, +}; + +void __init plat_time_init(void) +{ + int t; + + t = alchemy_get_cputype(); + if (t == ALCHEMY_CPU_UNKNOWN || + alchemy_time_init(alchemy_m2inttab[t])) + cpu_wait = NULL; /* wait doesn't work with r4k timer */ } diff --git a/arch/mips/alchemy/common/usb.c b/arch/mips/alchemy/common/usb.c new file mode 100644 index 00000000000..d193dbea84a --- /dev/null +++ b/arch/mips/alchemy/common/usb.c @@ -0,0 +1,593 @@ +/* + * USB block power/access management abstraction. + * + * Au1000+: The OHCI block control register is at the far end of the OHCI memory + * area. Au1550 has OHCI on different base address. No need to handle + * UDC here. + * Au1200: one register to control access and clocks to O/EHCI, UDC and OTG + * as well as the PHY for EHCI and UDC. + * + */ + +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/syscore_ops.h> +#include <asm/cpu.h> +#include <asm/mach-au1x00/au1000.h> + +/* control register offsets */ +#define AU1000_OHCICFG 0x7fffc +#define AU1550_OHCICFG 0x07ffc +#define AU1200_USBCFG 0x04 + +/* Au1000 USB block config bits */ +#define USBHEN_RD (1 << 4) /* OHCI reset-done indicator */ +#define USBHEN_CE (1 << 3) /* OHCI block clock enable */ +#define USBHEN_E (1 << 2) /* OHCI block enable */ +#define USBHEN_C (1 << 1) /* OHCI block coherency bit */ +#define USBHEN_BE (1 << 0) /* OHCI Big-Endian */ + +/* Au1200 USB config bits */ +#define USBCFG_PFEN (1 << 31) /* prefetch enable (undoc) */ +#define USBCFG_RDCOMB (1 << 30) /* read combining (undoc) */ +#define USBCFG_UNKNOWN (5 << 20) /* unknown, leave this way */ +#define USBCFG_SSD (1 << 23) /* serial short detect en */ +#define USBCFG_PPE (1 << 19) /* HS PHY PLL */ +#define USBCFG_UCE (1 << 18) /* UDC clock enable */ +#define USBCFG_ECE (1 << 17) /* EHCI clock enable */ +#define USBCFG_OCE (1 << 16) /* OHCI clock enable */ +#define USBCFG_FLA(x) (((x) & 0x3f) << 8) +#define USBCFG_UCAM (1 << 7) /* coherent access (undoc) */ +#define USBCFG_GME (1 << 6) /* OTG mem access */ +#define USBCFG_DBE (1 << 5) /* UDC busmaster enable */ +#define USBCFG_DME (1 << 4) /* UDC mem enable */ +#define USBCFG_EBE (1 << 3) /* EHCI busmaster enable */ +#define USBCFG_EME (1 << 2) /* EHCI mem enable */ +#define USBCFG_OBE (1 << 1) /* OHCI busmaster enable */ +#define USBCFG_OME (1 << 0) /* OHCI mem enable */ +#define USBCFG_INIT_AU1200 (USBCFG_PFEN | USBCFG_RDCOMB | USBCFG_UNKNOWN |\ + USBCFG_SSD | USBCFG_FLA(0x20) | USBCFG_UCAM | \ + USBCFG_GME | USBCFG_DBE | USBCFG_DME | \ + USBCFG_EBE | USBCFG_EME | USBCFG_OBE | \ + USBCFG_OME) + +/* Au1300 USB config registers */ +#define USB_DWC_CTRL1 0x00 +#define USB_DWC_CTRL2 0x04 +#define USB_VBUS_TIMER 0x10 +#define USB_SBUS_CTRL 0x14 +#define USB_MSR_ERR 0x18 +#define USB_DWC_CTRL3 0x1C +#define USB_DWC_CTRL4 0x20 +#define USB_OTG_STATUS 0x28 +#define USB_DWC_CTRL5 0x2C +#define USB_DWC_CTRL6 0x30 +#define USB_DWC_CTRL7 0x34 +#define USB_PHY_STATUS 0xC0 +#define USB_INT_STATUS 0xC4 +#define USB_INT_ENABLE 0xC8 + +#define USB_DWC_CTRL1_OTGD 0x04 /* set to DISable OTG */ +#define USB_DWC_CTRL1_HSTRS 0x02 /* set to ENable EHCI */ +#define USB_DWC_CTRL1_DCRS 0x01 /* set to ENable UDC */ + +#define USB_DWC_CTRL2_PHY1RS 0x04 /* set to enable PHY1 */ +#define USB_DWC_CTRL2_PHY0RS 0x02 /* set to enable PHY0 */ +#define USB_DWC_CTRL2_PHYRS 0x01 /* set to enable PHY */ + +#define USB_DWC_CTRL3_OHCI1_CKEN (1 << 19) +#define USB_DWC_CTRL3_OHCI0_CKEN (1 << 18) +#define USB_DWC_CTRL3_EHCI0_CKEN (1 << 17) +#define USB_DWC_CTRL3_OTG0_CKEN (1 << 16) + +#define USB_SBUS_CTRL_SBCA 0x04 /* coherent access */ + +#define USB_INTEN_FORCE 0x20 +#define USB_INTEN_PHY 0x10 +#define USB_INTEN_UDC 0x08 +#define USB_INTEN_EHCI 0x04 +#define USB_INTEN_OHCI1 0x02 +#define USB_INTEN_OHCI0 0x01 + +static DEFINE_SPINLOCK(alchemy_usb_lock); + +static inline void __au1300_usb_phyctl(void __iomem *base, int enable) +{ + unsigned long r, s; + + r = __raw_readl(base + USB_DWC_CTRL2); + s = __raw_readl(base + USB_DWC_CTRL3); + + s &= USB_DWC_CTRL3_OHCI1_CKEN | USB_DWC_CTRL3_OHCI0_CKEN | + USB_DWC_CTRL3_EHCI0_CKEN | USB_DWC_CTRL3_OTG0_CKEN; + + if (enable) { + /* simply enable all PHYs */ + r |= USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | + USB_DWC_CTRL2_PHYRS; + __raw_writel(r, base + USB_DWC_CTRL2); + wmb(); + } else if (!s) { + /* no USB block active, do disable all PHYs */ + r &= ~(USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | + USB_DWC_CTRL2_PHYRS); + __raw_writel(r, base + USB_DWC_CTRL2); + wmb(); + } +} + +static inline void __au1300_ohci_control(void __iomem *base, int enable, int id) +{ + unsigned long r; + + if (enable) { + __raw_writel(1, base + USB_DWC_CTRL7); /* start OHCI clock */ + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL3); /* enable OHCI block */ + r |= (id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN + : USB_DWC_CTRL3_OHCI1_CKEN; + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + __au1300_usb_phyctl(base, enable); /* power up the PHYs */ + + r = __raw_readl(base + USB_INT_ENABLE); + r |= (id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1; + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + + /* reset the OHCI start clock bit */ + __raw_writel(0, base + USB_DWC_CTRL7); + wmb(); + } else { + r = __raw_readl(base + USB_INT_ENABLE); + r &= ~((id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1); + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL3); + r &= ~((id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN + : USB_DWC_CTRL3_OHCI1_CKEN); + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + __au1300_usb_phyctl(base, enable); + } +} + +static inline void __au1300_ehci_control(void __iomem *base, int enable) +{ + unsigned long r; + + if (enable) { + r = __raw_readl(base + USB_DWC_CTRL3); + r |= USB_DWC_CTRL3_EHCI0_CKEN; + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL1); + r |= USB_DWC_CTRL1_HSTRS; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + __au1300_usb_phyctl(base, enable); + + r = __raw_readl(base + USB_INT_ENABLE); + r |= USB_INTEN_EHCI; + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + } else { + r = __raw_readl(base + USB_INT_ENABLE); + r &= ~USB_INTEN_EHCI; + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL1); + r &= ~USB_DWC_CTRL1_HSTRS; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL3); + r &= ~USB_DWC_CTRL3_EHCI0_CKEN; + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + __au1300_usb_phyctl(base, enable); + } +} + +static inline void __au1300_udc_control(void __iomem *base, int enable) +{ + unsigned long r; + + if (enable) { + r = __raw_readl(base + USB_DWC_CTRL1); + r |= USB_DWC_CTRL1_DCRS; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + __au1300_usb_phyctl(base, enable); + + r = __raw_readl(base + USB_INT_ENABLE); + r |= USB_INTEN_UDC; + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + } else { + r = __raw_readl(base + USB_INT_ENABLE); + r &= ~USB_INTEN_UDC; + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL1); + r &= ~USB_DWC_CTRL1_DCRS; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + __au1300_usb_phyctl(base, enable); + } +} + +static inline void __au1300_otg_control(void __iomem *base, int enable) +{ + unsigned long r; + if (enable) { + r = __raw_readl(base + USB_DWC_CTRL3); + r |= USB_DWC_CTRL3_OTG0_CKEN; + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL1); + r &= ~USB_DWC_CTRL1_OTGD; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + __au1300_usb_phyctl(base, enable); + } else { + r = __raw_readl(base + USB_DWC_CTRL1); + r |= USB_DWC_CTRL1_OTGD; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL3); + r &= ~USB_DWC_CTRL3_OTG0_CKEN; + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + __au1300_usb_phyctl(base, enable); + } +} + +static inline int au1300_usb_control(int block, int enable) +{ + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); + int ret = 0; + + switch (block) { + case ALCHEMY_USB_OHCI0: + __au1300_ohci_control(base, enable, 0); + break; + case ALCHEMY_USB_OHCI1: + __au1300_ohci_control(base, enable, 1); + break; + case ALCHEMY_USB_EHCI0: + __au1300_ehci_control(base, enable); + break; + case ALCHEMY_USB_UDC0: + __au1300_udc_control(base, enable); + break; + case ALCHEMY_USB_OTG0: + __au1300_otg_control(base, enable); + break; + default: + ret = -ENODEV; + } + return ret; +} + +static inline void au1300_usb_init(void) +{ + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); + + /* set some sane defaults. Note: we don't fiddle with DWC_CTRL4 + * here at all: Port 2 routing (EHCI or UDC) must be set either + * by boot firmware or platform init code; I can't autodetect + * a sane setting. + */ + __raw_writel(0, base + USB_INT_ENABLE); /* disable all USB irqs */ + wmb(); + __raw_writel(0, base + USB_DWC_CTRL3); /* disable all clocks */ + wmb(); + __raw_writel(~0, base + USB_MSR_ERR); /* clear all errors */ + wmb(); + __raw_writel(~0, base + USB_INT_STATUS); /* clear int status */ + wmb(); + /* set coherent access bit */ + __raw_writel(USB_SBUS_CTRL_SBCA, base + USB_SBUS_CTRL); + wmb(); +} + +static inline void __au1200_ohci_control(void __iomem *base, int enable) +{ + unsigned long r = __raw_readl(base + AU1200_USBCFG); + if (enable) { + __raw_writel(r | USBCFG_OCE, base + AU1200_USBCFG); + wmb(); + udelay(2000); + } else { + __raw_writel(r & ~USBCFG_OCE, base + AU1200_USBCFG); + wmb(); + udelay(1000); + } +} + +static inline void __au1200_ehci_control(void __iomem *base, int enable) +{ + unsigned long r = __raw_readl(base + AU1200_USBCFG); + if (enable) { + __raw_writel(r | USBCFG_ECE | USBCFG_PPE, base + AU1200_USBCFG); + wmb(); + udelay(1000); + } else { + if (!(r & USBCFG_UCE)) /* UDC also off? */ + r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */ + __raw_writel(r & ~USBCFG_ECE, base + AU1200_USBCFG); + wmb(); + udelay(1000); + } +} + +static inline void __au1200_udc_control(void __iomem *base, int enable) +{ + unsigned long r = __raw_readl(base + AU1200_USBCFG); + if (enable) { + __raw_writel(r | USBCFG_UCE | USBCFG_PPE, base + AU1200_USBCFG); + wmb(); + } else { + if (!(r & USBCFG_ECE)) /* EHCI also off? */ + r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */ + __raw_writel(r & ~USBCFG_UCE, base + AU1200_USBCFG); + wmb(); + } +} + +static inline int au1200_usb_control(int block, int enable) +{ + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR); + + switch (block) { + case ALCHEMY_USB_OHCI0: + __au1200_ohci_control(base, enable); + break; + case ALCHEMY_USB_UDC0: + __au1200_udc_control(base, enable); + break; + case ALCHEMY_USB_EHCI0: + __au1200_ehci_control(base, enable); + break; + default: + return -ENODEV; + } + return 0; +} + + +/* initialize USB block(s) to a known working state */ +static inline void au1200_usb_init(void) +{ + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR); + __raw_writel(USBCFG_INIT_AU1200, base + AU1200_USBCFG); + wmb(); + udelay(1000); +} + +static inline void au1000_usb_init(unsigned long rb, int reg) +{ + void __iomem *base = (void __iomem *)KSEG1ADDR(rb + reg); + unsigned long r = __raw_readl(base); + +#if defined(__BIG_ENDIAN) + r |= USBHEN_BE; +#endif + r |= USBHEN_C; + + __raw_writel(r, base); + wmb(); + udelay(1000); +} + + +static inline void __au1xx0_ohci_control(int enable, unsigned long rb, int creg) +{ + void __iomem *base = (void __iomem *)KSEG1ADDR(rb); + unsigned long r = __raw_readl(base + creg); + + if (enable) { + __raw_writel(r | USBHEN_CE, base + creg); + wmb(); + udelay(1000); + __raw_writel(r | USBHEN_CE | USBHEN_E, base + creg); + wmb(); + udelay(1000); + + /* wait for reset complete (read reg twice: au1500 erratum) */ + while (__raw_readl(base + creg), + !(__raw_readl(base + creg) & USBHEN_RD)) + udelay(1000); + } else { + __raw_writel(r & ~(USBHEN_CE | USBHEN_E), base + creg); + wmb(); + } +} + +static inline int au1000_usb_control(int block, int enable, unsigned long rb, + int creg) +{ + int ret = 0; + + switch (block) { + case ALCHEMY_USB_OHCI0: + __au1xx0_ohci_control(enable, rb, creg); + break; + default: + ret = -ENODEV; + } + return ret; +} + +/* + * alchemy_usb_control - control Alchemy on-chip USB blocks + * @block: USB block to target + * @enable: set 1 to enable a block, 0 to disable + */ +int alchemy_usb_control(int block, int enable) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&alchemy_usb_lock, flags); + switch (alchemy_get_cputype()) { + case ALCHEMY_CPU_AU1000: + case ALCHEMY_CPU_AU1500: + case ALCHEMY_CPU_AU1100: + ret = au1000_usb_control(block, enable, + AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG); + break; + case ALCHEMY_CPU_AU1550: + ret = au1000_usb_control(block, enable, + AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG); + break; + case ALCHEMY_CPU_AU1200: + ret = au1200_usb_control(block, enable); + break; + case ALCHEMY_CPU_AU1300: + ret = au1300_usb_control(block, enable); + break; + default: + ret = -ENODEV; + } + spin_unlock_irqrestore(&alchemy_usb_lock, flags); + return ret; +} +EXPORT_SYMBOL_GPL(alchemy_usb_control); + + +static unsigned long alchemy_usb_pmdata[2]; + +static void au1000_usb_pm(unsigned long br, int creg, int susp) +{ + void __iomem *base = (void __iomem *)KSEG1ADDR(br); + + if (susp) { + alchemy_usb_pmdata[0] = __raw_readl(base + creg); + /* There appears to be some undocumented reset register.... */ + __raw_writel(0, base + 0x04); + wmb(); + __raw_writel(0, base + creg); + wmb(); + } else { + __raw_writel(alchemy_usb_pmdata[0], base + creg); + wmb(); + } +} + +static void au1200_usb_pm(int susp) +{ + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1200_USB_OTG_PHYS_ADDR); + if (susp) { + /* save OTG_CAP/MUX registers which indicate port routing */ + /* FIXME: write an OTG driver to do that */ + alchemy_usb_pmdata[0] = __raw_readl(base + 0x00); + alchemy_usb_pmdata[1] = __raw_readl(base + 0x04); + } else { + /* restore access to all MMIO areas */ + au1200_usb_init(); + + /* restore OTG_CAP/MUX registers */ + __raw_writel(alchemy_usb_pmdata[0], base + 0x00); + __raw_writel(alchemy_usb_pmdata[1], base + 0x04); + wmb(); + } +} + +static void au1300_usb_pm(int susp) +{ + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); + /* remember Port2 routing */ + if (susp) { + alchemy_usb_pmdata[0] = __raw_readl(base + USB_DWC_CTRL4); + } else { + au1300_usb_init(); + __raw_writel(alchemy_usb_pmdata[0], base + USB_DWC_CTRL4); + wmb(); + } +} + +static void alchemy_usb_pm(int susp) +{ + switch (alchemy_get_cputype()) { + case ALCHEMY_CPU_AU1000: + case ALCHEMY_CPU_AU1500: + case ALCHEMY_CPU_AU1100: + au1000_usb_pm(AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG, susp); + break; + case ALCHEMY_CPU_AU1550: + au1000_usb_pm(AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG, susp); + break; + case ALCHEMY_CPU_AU1200: + au1200_usb_pm(susp); + break; + case ALCHEMY_CPU_AU1300: + au1300_usb_pm(susp); + break; + } +} + +static int alchemy_usb_suspend(void) +{ + alchemy_usb_pm(1); + return 0; +} + +static void alchemy_usb_resume(void) +{ + alchemy_usb_pm(0); +} + +static struct syscore_ops alchemy_usb_pm_ops = { + .suspend = alchemy_usb_suspend, + .resume = alchemy_usb_resume, +}; + +static int __init alchemy_usb_init(void) +{ + switch (alchemy_get_cputype()) { + case ALCHEMY_CPU_AU1000: + case ALCHEMY_CPU_AU1500: + case ALCHEMY_CPU_AU1100: + au1000_usb_init(AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG); + break; + case ALCHEMY_CPU_AU1550: + au1000_usb_init(AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG); + break; + case ALCHEMY_CPU_AU1200: + au1200_usb_init(); + break; + case ALCHEMY_CPU_AU1300: + au1300_usb_init(); + break; + } + + register_syscore_ops(&alchemy_usb_pm_ops); + + return 0; +} +arch_initcall(alchemy_usb_init); diff --git a/arch/mips/alchemy/common/vss.c b/arch/mips/alchemy/common/vss.c new file mode 100644 index 00000000000..d23b1444d36 --- /dev/null +++ b/arch/mips/alchemy/common/vss.c @@ -0,0 +1,84 @@ +/* + * Au1300 media block power gating (VSS) + * + * This is a stop-gap solution until I have the clock framework integration + * ready. This stuff here really must be handled transparently when clocks + * for various media blocks are enabled/disabled. + */ + +#include <linux/module.h> +#include <linux/spinlock.h> +#include <asm/mach-au1x00/au1000.h> + +#define VSS_GATE 0x00 /* gate wait timers */ +#define VSS_CLKRST 0x04 /* clock/block control */ +#define VSS_FTR 0x08 /* footers */ + +#define VSS_ADDR(blk) (KSEG1ADDR(AU1300_VSS_PHYS_ADDR) + (blk * 0x0c)) + +static DEFINE_SPINLOCK(au1300_vss_lock); + +/* enable a block as outlined in the databook */ +static inline void __enable_block(int block) +{ + void __iomem *base = (void __iomem *)VSS_ADDR(block); + + __raw_writel(3, base + VSS_CLKRST); /* enable clock, assert reset */ + wmb(); + + __raw_writel(0x01fffffe, base + VSS_GATE); /* maximum setup time */ + wmb(); + + /* enable footers in sequence */ + __raw_writel(0x01, base + VSS_FTR); + wmb(); + __raw_writel(0x03, base + VSS_FTR); + wmb(); + __raw_writel(0x07, base + VSS_FTR); + wmb(); + __raw_writel(0x0f, base + VSS_FTR); + wmb(); + + __raw_writel(0x01ffffff, base + VSS_GATE); /* start FSM too */ + wmb(); + + __raw_writel(2, base + VSS_CLKRST); /* deassert reset */ + wmb(); + + __raw_writel(0x1f, base + VSS_FTR); /* enable isolation cells */ + wmb(); +} + +/* disable a block as outlined in the databook */ +static inline void __disable_block(int block) +{ + void __iomem *base = (void __iomem *)VSS_ADDR(block); + + __raw_writel(0x0f, base + VSS_FTR); /* disable isolation cells */ + wmb(); + __raw_writel(0, base + VSS_GATE); /* disable FSM */ + wmb(); + __raw_writel(3, base + VSS_CLKRST); /* assert reset */ + wmb(); + __raw_writel(1, base + VSS_CLKRST); /* disable clock */ + wmb(); + __raw_writel(0, base + VSS_FTR); /* disable all footers */ + wmb(); +} + +void au1300_vss_block_control(int block, int enable) +{ + unsigned long flags; + + if (alchemy_get_cputype() != ALCHEMY_CPU_AU1300) + return; + + /* only one block at a time */ + spin_lock_irqsave(&au1300_vss_lock, flags); + if (enable) + __enable_block(block); + else + __disable_block(block); + spin_unlock_irqrestore(&au1300_vss_lock, flags); +} +EXPORT_SYMBOL_GPL(au1300_vss_block_control); |
