mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 08:48:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
53
arch/mips/alchemy/Kconfig
Normal file
53
arch/mips/alchemy/Kconfig
Normal file
|
@ -0,0 +1,53 @@
|
|||
# au1000-style gpio and interrupt controllers
|
||||
config ALCHEMY_GPIOINT_AU1000
|
||||
bool
|
||||
|
||||
# au1300-style GPIO/INT controller
|
||||
config ALCHEMY_GPIOINT_AU1300
|
||||
bool
|
||||
|
||||
# select this in your board config if you don't want to use the gpio
|
||||
# namespace as documented in the manuals. In this case however you need
|
||||
# to create the necessary gpio_* functions in your board code/headers!
|
||||
# see arch/mips/include/asm/mach-au1x00/gpio.h for more information.
|
||||
config ALCHEMY_GPIO_INDIRECT
|
||||
def_bool n
|
||||
|
||||
choice
|
||||
prompt "Machine type"
|
||||
depends on MIPS_ALCHEMY
|
||||
default MIPS_DB1XXX
|
||||
|
||||
config MIPS_MTX1
|
||||
bool "4G Systems MTX-1 board"
|
||||
select HW_HAS_PCI
|
||||
select ALCHEMY_GPIOINT_AU1000
|
||||
select SYS_SUPPORTS_LITTLE_ENDIAN
|
||||
select SYS_HAS_EARLY_PRINTK
|
||||
|
||||
config MIPS_DB1XXX
|
||||
bool "Alchemy DB1XXX / PB1XXX boards"
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select HW_HAS_PCI
|
||||
select SYS_SUPPORTS_LITTLE_ENDIAN
|
||||
select SYS_HAS_EARLY_PRINTK
|
||||
help
|
||||
Select this option if you have one of the following Alchemy
|
||||
development boards: DB1000 DB1500 DB1100 DB1550 DB1200 DB1300
|
||||
PB1500 PB1100 PB1550 PB1200
|
||||
Board type is autodetected during boot.
|
||||
|
||||
config MIPS_XXS1500
|
||||
bool "MyCable XXS1500 board"
|
||||
select ALCHEMY_GPIOINT_AU1000
|
||||
select SYS_SUPPORTS_LITTLE_ENDIAN
|
||||
select SYS_HAS_EARLY_PRINTK
|
||||
|
||||
config MIPS_GPR
|
||||
bool "Trapeze ITS GPR board"
|
||||
select ALCHEMY_GPIOINT_AU1000
|
||||
select HW_HAS_PCI
|
||||
select SYS_SUPPORTS_LITTLE_ENDIAN
|
||||
select SYS_HAS_EARLY_PRINTK
|
||||
|
||||
endchoice
|
3
arch/mips/alchemy/Makefile
Normal file
3
arch/mips/alchemy/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
obj-$(CONFIG_MIPS_GPR) += board-gpr.o
|
||||
obj-$(CONFIG_MIPS_MTX1) += board-mtx1.o
|
||||
obj-$(CONFIG_MIPS_XXS1500) += board-xxs1500.o
|
38
arch/mips/alchemy/Platform
Normal file
38
arch/mips/alchemy/Platform
Normal file
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
# Core Alchemy code
|
||||
#
|
||||
platform-$(CONFIG_MIPS_ALCHEMY) += alchemy/common/
|
||||
|
||||
|
||||
#
|
||||
# AMD Alchemy Db1000/Db1500/Pb1500/Db1100/Pb1100
|
||||
# Db1550/Pb1550/Db1200/Pb1200/Db1300
|
||||
#
|
||||
platform-$(CONFIG_MIPS_DB1XXX) += alchemy/devboards/
|
||||
cflags-$(CONFIG_MIPS_DB1XXX) += -I$(srctree)/arch/mips/include/asm/mach-db1x00
|
||||
load-$(CONFIG_MIPS_DB1XXX) += 0xffffffff80100000
|
||||
|
||||
#
|
||||
# 4G-Systems MTX-1 "MeshCube" wireless router
|
||||
#
|
||||
platform-$(CONFIG_MIPS_MTX1) += alchemy/
|
||||
load-$(CONFIG_MIPS_MTX1) += 0xffffffff80100000
|
||||
|
||||
#
|
||||
# MyCable eval board
|
||||
#
|
||||
platform-$(CONFIG_MIPS_XXS1500) += alchemy/
|
||||
load-$(CONFIG_MIPS_XXS1500) += 0xffffffff80100000
|
||||
|
||||
#
|
||||
# Trapeze ITS GRP board
|
||||
#
|
||||
platform-$(CONFIG_MIPS_GPR) += alchemy/
|
||||
load-$(CONFIG_MIPS_GPR) += 0xffffffff80100000
|
||||
|
||||
# boards can specify their own <gpio.h> in one of their include dirs.
|
||||
# If they do, placing this line here at the end will make sure the
|
||||
# compiler picks the board one. If they don't, it will make sure
|
||||
# the alchemy generic gpio header is picked up.
|
||||
|
||||
cflags-$(CONFIG_MIPS_ALCHEMY) += -I$(srctree)/arch/mips/include/asm/mach-au1x00
|
302
arch/mips/alchemy/board-gpr.c
Normal file
302
arch/mips/alchemy/board-gpr.c
Normal file
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* GPR board platform device registration (Au1550)
|
||||
*
|
||||
* Copyright (C) 2010 Wolfgang Grandegger <wg@denx.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-gpio.h>
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/idle.h>
|
||||
#include <asm/reboot.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <prom.h>
|
||||
|
||||
const char *get_system_type(void)
|
||||
{
|
||||
return "GPR";
|
||||
}
|
||||
|
||||
void __init prom_init(void)
|
||||
{
|
||||
unsigned char *memsize_str;
|
||||
unsigned long memsize;
|
||||
|
||||
prom_argc = fw_arg0;
|
||||
prom_argv = (char **)fw_arg1;
|
||||
prom_envp = (char **)fw_arg2;
|
||||
|
||||
prom_init_cmdline();
|
||||
|
||||
memsize_str = prom_getenv("memsize");
|
||||
if (!memsize_str || kstrtoul(memsize_str, 0, &memsize))
|
||||
memsize = 0x04000000;
|
||||
add_memory_region(0, memsize, BOOT_MEM_RAM);
|
||||
}
|
||||
|
||||
void prom_putchar(unsigned char c)
|
||||
{
|
||||
alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c);
|
||||
}
|
||||
|
||||
static void gpr_reset(char *c)
|
||||
{
|
||||
/* switch System-LED to orange (red# and green# on) */
|
||||
alchemy_gpio_direction_output(4, 0);
|
||||
alchemy_gpio_direction_output(5, 0);
|
||||
|
||||
/* trigger watchdog to reset board in 200ms */
|
||||
printk(KERN_EMERG "Triggering watchdog soft reset...\n");
|
||||
raw_local_irq_disable();
|
||||
alchemy_gpio_direction_output(1, 0);
|
||||
udelay(1);
|
||||
alchemy_gpio_set_value(1, 1);
|
||||
while (1)
|
||||
cpu_wait();
|
||||
}
|
||||
|
||||
static void gpr_power_off(void)
|
||||
{
|
||||
while (1)
|
||||
cpu_wait();
|
||||
}
|
||||
|
||||
void __init board_setup(void)
|
||||
{
|
||||
printk(KERN_INFO "Trapeze ITS GPR board\n");
|
||||
|
||||
pm_power_off = gpr_power_off;
|
||||
_machine_halt = gpr_power_off;
|
||||
_machine_restart = gpr_reset;
|
||||
|
||||
/* Enable UART1/3 */
|
||||
alchemy_uart_enable(AU1000_UART3_PHYS_ADDR);
|
||||
alchemy_uart_enable(AU1000_UART1_PHYS_ADDR);
|
||||
|
||||
/* Take away Reset of UMTS-card */
|
||||
alchemy_gpio_direction_output(215, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Watchdog
|
||||
*/
|
||||
static struct resource gpr_wdt_resource[] = {
|
||||
[0] = {
|
||||
.start = 1,
|
||||
.end = 1,
|
||||
.name = "gpr-adm6320-wdt",
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device gpr_wdt_device = {
|
||||
.name = "adm6320-wdt",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(gpr_wdt_resource),
|
||||
.resource = gpr_wdt_resource,
|
||||
};
|
||||
|
||||
/*
|
||||
* FLASH
|
||||
*
|
||||
* 0x00000000-0x00200000 : "kernel"
|
||||
* 0x00200000-0x00a00000 : "rootfs"
|
||||
* 0x01d00000-0x01f00000 : "config"
|
||||
* 0x01c00000-0x01d00000 : "yamon"
|
||||
* 0x01d00000-0x01d40000 : "yamon env vars"
|
||||
* 0x00000000-0x00a00000 : "kernel+rootfs"
|
||||
*/
|
||||
static struct mtd_partition gpr_mtd_partitions[] = {
|
||||
{
|
||||
.name = "kernel",
|
||||
.size = 0x00200000,
|
||||
.offset = 0,
|
||||
},
|
||||
{
|
||||
.name = "rootfs",
|
||||
.size = 0x00800000,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.mask_flags = MTD_WRITEABLE,
|
||||
},
|
||||
{
|
||||
.name = "config",
|
||||
.size = 0x00200000,
|
||||
.offset = 0x01d00000,
|
||||
},
|
||||
{
|
||||
.name = "yamon",
|
||||
.size = 0x00100000,
|
||||
.offset = 0x01c00000,
|
||||
},
|
||||
{
|
||||
.name = "yamon env vars",
|
||||
.size = 0x00040000,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
},
|
||||
{
|
||||
.name = "kernel+rootfs",
|
||||
.size = 0x00a00000,
|
||||
.offset = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static struct physmap_flash_data gpr_flash_data = {
|
||||
.width = 4,
|
||||
.nr_parts = ARRAY_SIZE(gpr_mtd_partitions),
|
||||
.parts = gpr_mtd_partitions,
|
||||
};
|
||||
|
||||
static struct resource gpr_mtd_resource = {
|
||||
.start = 0x1e000000,
|
||||
.end = 0x1fffffff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
static struct platform_device gpr_mtd_device = {
|
||||
.name = "physmap-flash",
|
||||
.dev = {
|
||||
.platform_data = &gpr_flash_data,
|
||||
},
|
||||
.num_resources = 1,
|
||||
.resource = &gpr_mtd_resource,
|
||||
};
|
||||
|
||||
/*
|
||||
* LEDs
|
||||
*/
|
||||
static struct gpio_led gpr_gpio_leds[] = {
|
||||
{ /* green */
|
||||
.name = "gpr:green",
|
||||
.gpio = 4,
|
||||
.active_low = 1,
|
||||
},
|
||||
{ /* red */
|
||||
.name = "gpr:red",
|
||||
.gpio = 5,
|
||||
.active_low = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static struct gpio_led_platform_data gpr_led_data = {
|
||||
.num_leds = ARRAY_SIZE(gpr_gpio_leds),
|
||||
.leds = gpr_gpio_leds,
|
||||
};
|
||||
|
||||
static struct platform_device gpr_led_devices = {
|
||||
.name = "leds-gpio",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &gpr_led_data,
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* I2C
|
||||
*/
|
||||
static struct i2c_gpio_platform_data gpr_i2c_data = {
|
||||
.sda_pin = 209,
|
||||
.sda_is_open_drain = 1,
|
||||
.scl_pin = 210,
|
||||
.scl_is_open_drain = 1,
|
||||
.udelay = 2, /* ~100 kHz */
|
||||
.timeout = HZ,
|
||||
};
|
||||
|
||||
static struct platform_device gpr_i2c_device = {
|
||||
.name = "i2c-gpio",
|
||||
.id = -1,
|
||||
.dev.platform_data = &gpr_i2c_data,
|
||||
};
|
||||
|
||||
static struct i2c_board_info gpr_i2c_info[] __initdata = {
|
||||
{
|
||||
I2C_BOARD_INFO("lm83", 0x18),
|
||||
.type = "lm83"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
static struct resource alchemy_pci_host_res[] = {
|
||||
[0] = {
|
||||
.start = AU1500_PCI_PHYS_ADDR,
|
||||
.end = AU1500_PCI_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static int gpr_map_pci_irq(const struct pci_dev *d, u8 slot, u8 pin)
|
||||
{
|
||||
if ((slot == 0) && (pin == 1))
|
||||
return AU1550_PCI_INTA;
|
||||
else if ((slot == 0) && (pin == 2))
|
||||
return AU1550_PCI_INTB;
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
static struct alchemy_pci_platdata gpr_pci_pd = {
|
||||
.board_map_irq = gpr_map_pci_irq,
|
||||
.pci_cfg_set = PCI_CONFIG_AEN | PCI_CONFIG_R2H | PCI_CONFIG_R1H |
|
||||
PCI_CONFIG_CH |
|
||||
#if defined(__MIPSEB__)
|
||||
PCI_CONFIG_SIC_HWA_DAT | PCI_CONFIG_SM,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct platform_device gpr_pci_host_dev = {
|
||||
.dev.platform_data = &gpr_pci_pd,
|
||||
.name = "alchemy-pci",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(alchemy_pci_host_res),
|
||||
.resource = alchemy_pci_host_res,
|
||||
};
|
||||
|
||||
static struct platform_device *gpr_devices[] __initdata = {
|
||||
&gpr_wdt_device,
|
||||
&gpr_mtd_device,
|
||||
&gpr_i2c_device,
|
||||
&gpr_led_devices,
|
||||
};
|
||||
|
||||
static int __init gpr_pci_init(void)
|
||||
{
|
||||
return platform_device_register(&gpr_pci_host_dev);
|
||||
}
|
||||
/* must be arch_initcall; MIPS PCI scans busses in a subsys_initcall */
|
||||
arch_initcall(gpr_pci_init);
|
||||
|
||||
|
||||
static int __init gpr_dev_init(void)
|
||||
{
|
||||
i2c_register_board_info(0, gpr_i2c_info, ARRAY_SIZE(gpr_i2c_info));
|
||||
|
||||
return platform_add_devices(gpr_devices, ARRAY_SIZE(gpr_devices));
|
||||
}
|
||||
device_initcall(gpr_dev_init);
|
313
arch/mips/alchemy/board-mtx1.c
Normal file
313
arch/mips/alchemy/board-mtx1.c
Normal file
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* MTX-1 platform devices registration (Au1500)
|
||||
*
|
||||
* Copyright (C) 2007-2009, Florian Fainelli <florian@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <mtd/mtd-abi.h>
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/reboot.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-au1x00/au1xxx_eth.h>
|
||||
#include <prom.h>
|
||||
|
||||
const char *get_system_type(void)
|
||||
{
|
||||
return "MTX-1";
|
||||
}
|
||||
|
||||
void __init prom_init(void)
|
||||
{
|
||||
unsigned char *memsize_str;
|
||||
unsigned long memsize;
|
||||
|
||||
prom_argc = fw_arg0;
|
||||
prom_argv = (char **)fw_arg1;
|
||||
prom_envp = (char **)fw_arg2;
|
||||
|
||||
prom_init_cmdline();
|
||||
|
||||
memsize_str = prom_getenv("memsize");
|
||||
if (!memsize_str || kstrtoul(memsize_str, 0, &memsize))
|
||||
memsize = 0x04000000;
|
||||
add_memory_region(0, memsize, BOOT_MEM_RAM);
|
||||
}
|
||||
|
||||
void prom_putchar(unsigned char c)
|
||||
{
|
||||
alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c);
|
||||
}
|
||||
|
||||
static void mtx1_reset(char *c)
|
||||
{
|
||||
/* Jump to the reset vector */
|
||||
__asm__ __volatile__("jr\t%0" : : "r"(0xbfc00000));
|
||||
}
|
||||
|
||||
static void mtx1_power_off(void)
|
||||
{
|
||||
while (1)
|
||||
asm volatile (
|
||||
" .set mips32 \n"
|
||||
" wait \n"
|
||||
" .set mips0 \n");
|
||||
}
|
||||
|
||||
void __init board_setup(void)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_USB_OHCI_HCD)
|
||||
/* Enable USB power switch */
|
||||
alchemy_gpio_direction_output(204, 0);
|
||||
#endif /* IS_ENABLED(CONFIG_USB_OHCI_HCD) */
|
||||
|
||||
/* Initialize sys_pinfunc */
|
||||
alchemy_wrsys(SYS_PF_NI2, AU1000_SYS_PINFUNC);
|
||||
|
||||
/* Initialize GPIO */
|
||||
alchemy_wrsys(~0, AU1000_SYS_TRIOUTCLR);
|
||||
alchemy_gpio_direction_output(0, 0); /* Disable M66EN (PCI 66MHz) */
|
||||
alchemy_gpio_direction_output(3, 1); /* Disable PCI CLKRUN# */
|
||||
alchemy_gpio_direction_output(1, 1); /* Enable EXT_IO3 */
|
||||
alchemy_gpio_direction_output(5, 0); /* Disable eth PHY TX_ER */
|
||||
|
||||
/* Enable LED and set it to green */
|
||||
alchemy_gpio_direction_output(211, 1); /* green on */
|
||||
alchemy_gpio_direction_output(212, 0); /* red off */
|
||||
|
||||
pm_power_off = mtx1_power_off;
|
||||
_machine_halt = mtx1_power_off;
|
||||
_machine_restart = mtx1_reset;
|
||||
|
||||
printk(KERN_INFO "4G Systems MTX-1 Board\n");
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static struct gpio_keys_button mtx1_gpio_button[] = {
|
||||
{
|
||||
.gpio = 207,
|
||||
.code = BTN_0,
|
||||
.desc = "System button",
|
||||
}
|
||||
};
|
||||
|
||||
static struct gpio_keys_platform_data mtx1_buttons_data = {
|
||||
.buttons = mtx1_gpio_button,
|
||||
.nbuttons = ARRAY_SIZE(mtx1_gpio_button),
|
||||
};
|
||||
|
||||
static struct platform_device mtx1_button = {
|
||||
.name = "gpio-keys",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &mtx1_buttons_data,
|
||||
}
|
||||
};
|
||||
|
||||
static struct resource mtx1_wdt_res[] = {
|
||||
[0] = {
|
||||
.start = 215,
|
||||
.end = 215,
|
||||
.name = "mtx1-wdt-gpio",
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device mtx1_wdt = {
|
||||
.name = "mtx1-wdt",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(mtx1_wdt_res),
|
||||
.resource = mtx1_wdt_res,
|
||||
};
|
||||
|
||||
static struct gpio_led default_leds[] = {
|
||||
{
|
||||
.name = "mtx1:green",
|
||||
.gpio = 211,
|
||||
}, {
|
||||
.name = "mtx1:red",
|
||||
.gpio = 212,
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_led_platform_data mtx1_led_data = {
|
||||
.num_leds = ARRAY_SIZE(default_leds),
|
||||
.leds = default_leds,
|
||||
};
|
||||
|
||||
static struct platform_device mtx1_gpio_leds = {
|
||||
.name = "leds-gpio",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &mtx1_led_data,
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_partition mtx1_mtd_partitions[] = {
|
||||
{
|
||||
.name = "filesystem",
|
||||
.size = 0x01C00000,
|
||||
.offset = 0,
|
||||
},
|
||||
{
|
||||
.name = "yamon",
|
||||
.size = 0x00100000,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.mask_flags = MTD_WRITEABLE,
|
||||
},
|
||||
{
|
||||
.name = "kernel",
|
||||
.size = 0x002c0000,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
},
|
||||
{
|
||||
.name = "yamon env",
|
||||
.size = 0x00040000,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
},
|
||||
};
|
||||
|
||||
static struct physmap_flash_data mtx1_flash_data = {
|
||||
.width = 4,
|
||||
.nr_parts = 4,
|
||||
.parts = mtx1_mtd_partitions,
|
||||
};
|
||||
|
||||
static struct resource mtx1_mtd_resource = {
|
||||
.start = 0x1e000000,
|
||||
.end = 0x1fffffff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
static struct platform_device mtx1_mtd = {
|
||||
.name = "physmap-flash",
|
||||
.dev = {
|
||||
.platform_data = &mtx1_flash_data,
|
||||
},
|
||||
.num_resources = 1,
|
||||
.resource = &mtx1_mtd_resource,
|
||||
};
|
||||
|
||||
static struct resource alchemy_pci_host_res[] = {
|
||||
[0] = {
|
||||
.start = AU1500_PCI_PHYS_ADDR,
|
||||
.end = AU1500_PCI_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static int mtx1_pci_idsel(unsigned int devsel, int assert)
|
||||
{
|
||||
/* This function is only necessary to support a proprietary Cardbus
|
||||
* adapter on the mtx-1 "singleboard" variant. It triggers a custom
|
||||
* logic chip connected to EXT_IO3 (GPIO1) to suppress IDSEL signals.
|
||||
*/
|
||||
udelay(1);
|
||||
|
||||
if (assert && devsel != 0)
|
||||
/* Suppress signal to Cardbus */
|
||||
alchemy_gpio_set_value(1, 0); /* set EXT_IO3 OFF */
|
||||
else
|
||||
alchemy_gpio_set_value(1, 1); /* set EXT_IO3 ON */
|
||||
|
||||
udelay(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char mtx1_irqtab[][5] = {
|
||||
[0] = { -1, AU1500_PCI_INTA, AU1500_PCI_INTA, 0xff, 0xff }, /* IDSEL 00 - AdapterA-Slot0 (top) */
|
||||
[1] = { -1, AU1500_PCI_INTB, AU1500_PCI_INTA, 0xff, 0xff }, /* IDSEL 01 - AdapterA-Slot1 (bottom) */
|
||||
[2] = { -1, AU1500_PCI_INTC, AU1500_PCI_INTD, 0xff, 0xff }, /* IDSEL 02 - AdapterB-Slot0 (top) */
|
||||
[3] = { -1, AU1500_PCI_INTD, AU1500_PCI_INTC, 0xff, 0xff }, /* IDSEL 03 - AdapterB-Slot1 (bottom) */
|
||||
[4] = { -1, AU1500_PCI_INTA, AU1500_PCI_INTB, 0xff, 0xff }, /* IDSEL 04 - AdapterC-Slot0 (top) */
|
||||
[5] = { -1, AU1500_PCI_INTB, AU1500_PCI_INTA, 0xff, 0xff }, /* IDSEL 05 - AdapterC-Slot1 (bottom) */
|
||||
[6] = { -1, AU1500_PCI_INTC, AU1500_PCI_INTD, 0xff, 0xff }, /* IDSEL 06 - AdapterD-Slot0 (top) */
|
||||
[7] = { -1, AU1500_PCI_INTD, AU1500_PCI_INTC, 0xff, 0xff }, /* IDSEL 07 - AdapterD-Slot1 (bottom) */
|
||||
};
|
||||
|
||||
static int mtx1_map_pci_irq(const struct pci_dev *d, u8 slot, u8 pin)
|
||||
{
|
||||
return mtx1_irqtab[slot][pin];
|
||||
}
|
||||
|
||||
static struct alchemy_pci_platdata mtx1_pci_pd = {
|
||||
.board_map_irq = mtx1_map_pci_irq,
|
||||
.board_pci_idsel = mtx1_pci_idsel,
|
||||
.pci_cfg_set = PCI_CONFIG_AEN | PCI_CONFIG_R2H | PCI_CONFIG_R1H |
|
||||
PCI_CONFIG_CH |
|
||||
#if defined(__MIPSEB__)
|
||||
PCI_CONFIG_SIC_HWA_DAT | PCI_CONFIG_SM,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct platform_device mtx1_pci_host = {
|
||||
.dev.platform_data = &mtx1_pci_pd,
|
||||
.name = "alchemy-pci",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(alchemy_pci_host_res),
|
||||
.resource = alchemy_pci_host_res,
|
||||
};
|
||||
|
||||
static struct platform_device *mtx1_devs[] __initdata = {
|
||||
&mtx1_pci_host,
|
||||
&mtx1_gpio_leds,
|
||||
&mtx1_wdt,
|
||||
&mtx1_button,
|
||||
&mtx1_mtd,
|
||||
};
|
||||
|
||||
static struct au1000_eth_platform_data mtx1_au1000_eth0_pdata = {
|
||||
.phy_search_highest_addr = 1,
|
||||
.phy1_search_mac0 = 1,
|
||||
};
|
||||
|
||||
static int __init mtx1_register_devices(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
irq_set_irq_type(AU1500_GPIO204_INT, IRQ_TYPE_LEVEL_HIGH);
|
||||
irq_set_irq_type(AU1500_GPIO201_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO202_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO203_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO205_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
|
||||
au1xxx_override_eth_cfg(0, &mtx1_au1000_eth0_pdata);
|
||||
|
||||
rc = gpio_request(mtx1_gpio_button[0].gpio,
|
||||
mtx1_gpio_button[0].desc);
|
||||
if (rc < 0) {
|
||||
printk(KERN_INFO "mtx1: failed to request %d\n",
|
||||
mtx1_gpio_button[0].gpio);
|
||||
goto out;
|
||||
}
|
||||
gpio_direction_input(mtx1_gpio_button[0].gpio);
|
||||
out:
|
||||
return platform_add_devices(mtx1_devs, ARRAY_SIZE(mtx1_devs));
|
||||
}
|
||||
arch_initcall(mtx1_register_devices);
|
154
arch/mips/alchemy/board-xxs1500.c
Normal file
154
arch/mips/alchemy/board-xxs1500.c
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* BRIEF MODULE DESCRIPTION
|
||||
* MyCable XXS1500 board support
|
||||
*
|
||||
* Copyright 2003, 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/reboot.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <prom.h>
|
||||
|
||||
const char *get_system_type(void)
|
||||
{
|
||||
return "XXS1500";
|
||||
}
|
||||
|
||||
void __init prom_init(void)
|
||||
{
|
||||
unsigned char *memsize_str;
|
||||
unsigned long memsize;
|
||||
|
||||
prom_argc = fw_arg0;
|
||||
prom_argv = (char **)fw_arg1;
|
||||
prom_envp = (char **)fw_arg2;
|
||||
|
||||
prom_init_cmdline();
|
||||
|
||||
memsize_str = prom_getenv("memsize");
|
||||
if (!memsize_str || kstrtoul(memsize_str, 0, &memsize))
|
||||
memsize = 0x04000000;
|
||||
|
||||
add_memory_region(0, memsize, BOOT_MEM_RAM);
|
||||
}
|
||||
|
||||
void prom_putchar(unsigned char c)
|
||||
{
|
||||
alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c);
|
||||
}
|
||||
|
||||
static void xxs1500_reset(char *c)
|
||||
{
|
||||
/* Jump to the reset vector */
|
||||
__asm__ __volatile__("jr\t%0" : : "r"(0xbfc00000));
|
||||
}
|
||||
|
||||
static void xxs1500_power_off(void)
|
||||
{
|
||||
while (1)
|
||||
asm volatile (
|
||||
" .set mips32 \n"
|
||||
" wait \n"
|
||||
" .set mips0 \n");
|
||||
}
|
||||
|
||||
void __init board_setup(void)
|
||||
{
|
||||
u32 pin_func;
|
||||
|
||||
pm_power_off = xxs1500_power_off;
|
||||
_machine_halt = xxs1500_power_off;
|
||||
_machine_restart = xxs1500_reset;
|
||||
|
||||
alchemy_gpio1_input_enable();
|
||||
alchemy_gpio2_enable();
|
||||
|
||||
/* Set multiple use pins (UART3/GPIO) to UART (it's used as UART too) */
|
||||
pin_func = alchemy_rdsys(AU1000_SYS_PINFUNC) & ~SYS_PF_UR3;
|
||||
pin_func |= SYS_PF_UR3;
|
||||
alchemy_wrsys(pin_func, AU1000_SYS_PINFUNC);
|
||||
|
||||
/* Enable UART */
|
||||
alchemy_uart_enable(AU1000_UART3_PHYS_ADDR);
|
||||
/* Enable DTR (MCR bit 0) = USB power up */
|
||||
__raw_writel(1, (void __iomem *)KSEG1ADDR(AU1000_UART3_PHYS_ADDR + 0x18));
|
||||
wmb();
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static struct resource xxs1500_pcmcia_res[] = {
|
||||
{
|
||||
.name = "pcmcia-io",
|
||||
.flags = IORESOURCE_MEM,
|
||||
.start = AU1000_PCMCIA_IO_PHYS_ADDR,
|
||||
.end = AU1000_PCMCIA_IO_PHYS_ADDR + 0x000400000 - 1,
|
||||
},
|
||||
{
|
||||
.name = "pcmcia-attr",
|
||||
.flags = IORESOURCE_MEM,
|
||||
.start = AU1000_PCMCIA_ATTR_PHYS_ADDR,
|
||||
.end = AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x000400000 - 1,
|
||||
},
|
||||
{
|
||||
.name = "pcmcia-mem",
|
||||
.flags = IORESOURCE_MEM,
|
||||
.start = AU1000_PCMCIA_MEM_PHYS_ADDR,
|
||||
.end = AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device xxs1500_pcmcia_dev = {
|
||||
.name = "xxs1500_pcmcia",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(xxs1500_pcmcia_res),
|
||||
.resource = xxs1500_pcmcia_res,
|
||||
};
|
||||
|
||||
static struct platform_device *xxs1500_devs[] __initdata = {
|
||||
&xxs1500_pcmcia_dev,
|
||||
};
|
||||
|
||||
static int __init xxs1500_dev_init(void)
|
||||
{
|
||||
irq_set_irq_type(AU1500_GPIO204_INT, IRQ_TYPE_LEVEL_HIGH);
|
||||
irq_set_irq_type(AU1500_GPIO201_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO202_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO203_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO205_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO207_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
|
||||
irq_set_irq_type(AU1500_GPIO0_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO1_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO2_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO3_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO4_INT, IRQ_TYPE_LEVEL_LOW); /* CF irq */
|
||||
irq_set_irq_type(AU1500_GPIO5_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
|
||||
return platform_add_devices(xxs1500_devs,
|
||||
ARRAY_SIZE(xxs1500_devs));
|
||||
}
|
||||
device_initcall(xxs1500_dev_init);
|
14
arch/mips/alchemy/common/Makefile
Normal file
14
arch/mips/alchemy/common/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# Copyright 2000, 2008 MontaVista Software Inc.
|
||||
# Author: MontaVista Software, Inc. <source@mvista.com>
|
||||
#
|
||||
# Makefile for the Alchemy Au1xx0 CPUs, generic files.
|
||||
#
|
||||
|
||||
obj-y += prom.o time.o clock.o platform.o power.o \
|
||||
setup.o sleeper.o dma.o dbdma.o vss.o irq.o usb.o
|
||||
|
||||
# optional gpiolib support
|
||||
ifeq ($(CONFIG_ALCHEMY_GPIO_INDIRECT),)
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib.o
|
||||
endif
|
1096
arch/mips/alchemy/common/clock.c
Normal file
1096
arch/mips/alchemy/common/clock.c
Normal file
File diff suppressed because it is too large
Load diff
1089
arch/mips/alchemy/common/dbdma.c
Normal file
1089
arch/mips/alchemy/common/dbdma.c
Normal file
File diff suppressed because it is too large
Load diff
265
arch/mips/alchemy/common/dma.c
Normal file
265
arch/mips/alchemy/common/dma.c
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
*
|
||||
* BRIEF MODULE DESCRIPTION
|
||||
* A DMA channel allocator for Au1x00. API is modeled loosely off of
|
||||
* linux/kernel/dma.c.
|
||||
*
|
||||
* Copyright 2000, 2008 MontaVista Software Inc.
|
||||
* Author: MontaVista Software, Inc. <source@mvista.com>
|
||||
* Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org)
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-au1x00/au1000_dma.h>
|
||||
|
||||
/*
|
||||
* A note on resource allocation:
|
||||
*
|
||||
* All drivers needing DMA channels, should allocate and release them
|
||||
* through the public routines `request_dma()' and `free_dma()'.
|
||||
*
|
||||
* In order to avoid problems, all processes should allocate resources in
|
||||
* the same sequence and release them in the reverse order.
|
||||
*
|
||||
* So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ.
|
||||
* When releasing them, first release the IRQ, then release the DMA. The
|
||||
* main reason for this order is that, if you are requesting the DMA buffer
|
||||
* done interrupt, you won't know the irq number until the DMA channel is
|
||||
* 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] = {
|
||||
{.dev_id = -1,},
|
||||
{.dev_id = -1,},
|
||||
{.dev_id = -1,},
|
||||
{.dev_id = -1,},
|
||||
{.dev_id = -1,},
|
||||
{.dev_id = -1,},
|
||||
{.dev_id = -1,},
|
||||
{.dev_id = -1,}
|
||||
};
|
||||
EXPORT_SYMBOL(au1000_dma_table);
|
||||
|
||||
/* Device FIFO addresses and default DMA modes */
|
||||
static const struct dma_dev {
|
||||
unsigned int fifo_addr;
|
||||
unsigned int dma_mode;
|
||||
} dma_dev_table[DMA_NUM_DEV] = {
|
||||
{ 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,
|
||||
int length, int *eof, void *data)
|
||||
{
|
||||
int i, len = 0;
|
||||
struct dma_chan *chan;
|
||||
|
||||
for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) {
|
||||
chan = get_dma_chan(i);
|
||||
if (chan != NULL)
|
||||
len += sprintf(buf + len, "%2d: %s\n",
|
||||
i, chan->dev_str);
|
||||
}
|
||||
|
||||
if (fpos >= len) {
|
||||
*start = buf;
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
*start = buf + fpos;
|
||||
len -= fpos;
|
||||
if (len > length)
|
||||
return length;
|
||||
*eof = 1;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Device FIFO addresses and default DMA modes - 2nd bank */
|
||||
static const struct dma_dev dma_dev_table_bank2[DMA_NUM_DEV_BANK2] = {
|
||||
{ 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)
|
||||
{
|
||||
struct dma_chan *chan;
|
||||
|
||||
if (dmanr >= NUM_AU1000_DMA_CHANNELS)
|
||||
return;
|
||||
chan = &au1000_dma_table[dmanr];
|
||||
|
||||
printk(KERN_INFO "Au1000 DMA%d Register Dump:\n", dmanr);
|
||||
printk(KERN_INFO " mode = 0x%08x\n",
|
||||
__raw_readl(chan->io + DMA_MODE_SET));
|
||||
printk(KERN_INFO " addr = 0x%08x\n",
|
||||
__raw_readl(chan->io + DMA_PERIPHERAL_ADDR));
|
||||
printk(KERN_INFO " start0 = 0x%08x\n",
|
||||
__raw_readl(chan->io + DMA_BUFFER0_START));
|
||||
printk(KERN_INFO " start1 = 0x%08x\n",
|
||||
__raw_readl(chan->io + DMA_BUFFER1_START));
|
||||
printk(KERN_INFO " count0 = 0x%08x\n",
|
||||
__raw_readl(chan->io + DMA_BUFFER0_COUNT));
|
||||
printk(KERN_INFO " count1 = 0x%08x\n",
|
||||
__raw_readl(chan->io + DMA_BUFFER1_COUNT));
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds a free channel, and binds the requested device to it.
|
||||
* Returns the allocated channel number, or negative on error.
|
||||
* Requests the DMA done IRQ if irqhandler != NULL.
|
||||
*/
|
||||
int request_au1000_dma(int dev_id, const char *dev_str,
|
||||
irq_handler_t irqhandler,
|
||||
unsigned long irqflags,
|
||||
void *irq_dev_id)
|
||||
{
|
||||
struct dma_chan *chan;
|
||||
const struct dma_dev *dev;
|
||||
int i, ret;
|
||||
|
||||
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)
|
||||
break;
|
||||
|
||||
if (i == NUM_AU1000_DMA_CHANNELS)
|
||||
return -ENODEV;
|
||||
|
||||
chan = &au1000_dma_table[i];
|
||||
|
||||
if (dev_id >= DMA_NUM_DEV) {
|
||||
dev_id -= DMA_NUM_DEV;
|
||||
dev = &dma_dev_table_bank2[dev_id];
|
||||
} else
|
||||
dev = &dma_dev_table[dev_id];
|
||||
|
||||
if (irqhandler) {
|
||||
chan->irq_dev = irq_dev_id;
|
||||
ret = request_irq(chan->irq, irqhandler, irqflags, dev_str,
|
||||
chan->irq_dev);
|
||||
if (ret) {
|
||||
chan->irq_dev = NULL;
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
chan->irq_dev = NULL;
|
||||
}
|
||||
|
||||
/* fill it in */
|
||||
chan->io = (void __iomem *)(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;
|
||||
chan->mode = dev->dma_mode;
|
||||
|
||||
/* initialize the channel before returning */
|
||||
init_dma(i);
|
||||
|
||||
return i;
|
||||
}
|
||||
EXPORT_SYMBOL(request_au1000_dma);
|
||||
|
||||
void free_au1000_dma(unsigned int dmanr)
|
||||
{
|
||||
struct dma_chan *chan = get_dma_chan(dmanr);
|
||||
|
||||
if (!chan) {
|
||||
printk(KERN_ERR "Error trying to free DMA%d\n", dmanr);
|
||||
return;
|
||||
}
|
||||
|
||||
disable_dma(dmanr);
|
||||
if (chan->irq_dev)
|
||||
free_irq(chan->irq, chan->irq_dev);
|
||||
|
||||
chan->irq_dev = NULL;
|
||||
chan->dev_id = -1;
|
||||
}
|
||||
EXPORT_SYMBOL(free_au1000_dma);
|
||||
|
||||
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);
|
175
arch/mips/alchemy/common/gpiolib.c
Normal file
175
arch/mips/alchemy/common/gpiolib.c
Normal file
|
@ -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);
|
996
arch/mips/alchemy/common/irq.c
Normal file
996
arch/mips/alchemy/common/irq.c
Normal file
|
@ -0,0 +1,996 @@
|
|||
/*
|
||||
* Copyright 2001, 2007-2008 MontaVista Software Inc.
|
||||
* Author: MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* Copyright (C) 2007 Ralf Baechle (ralf@linux-mips.org)
|
||||
*
|
||||
* 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/export.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#include <asm/irq_cpu.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#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 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);
|
||||
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
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(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_MASKCLR);
|
||||
__raw_writel(1 << bit, base + IC_WAKECLR);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static void au1x_ic1_mask(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_MASKCLR);
|
||||
__raw_writel(1 << bit, base + IC_WAKECLR);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static void au1x_ic0_ack(struct irq_data *d)
|
||||
{
|
||||
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.
|
||||
*/
|
||||
__raw_writel(1 << bit, base + IC_FALLINGCLR);
|
||||
__raw_writel(1 << bit, base + IC_RISINGCLR);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static void au1x_ic1_ack(struct irq_data *d)
|
||||
{
|
||||
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.
|
||||
*/
|
||||
__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(struct irq_data *d, unsigned int on)
|
||||
{
|
||||
int bit = d->irq - AU1000_INTC1_INT_BASE;
|
||||
unsigned long wakemsk, flags;
|
||||
|
||||
/* 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 = alchemy_rdsys(AU1000_SYS_WAKEMSK);
|
||||
if (on)
|
||||
wakemsk |= 1 << bit;
|
||||
else
|
||||
wakemsk &= ~(1 << bit);
|
||||
alchemy_wrsys(wakemsk, AU1000_SYS_WAKEMSK);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* irq_chips for both ICs; this way the mask handlers can be
|
||||
* as short as possible.
|
||||
*/
|
||||
static struct irq_chip au1x_ic0_chip = {
|
||||
.name = "Alchemy-IC0",
|
||||
.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",
|
||||
.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(struct irq_data *d, unsigned int flow_type)
|
||||
{
|
||||
struct irq_chip *chip;
|
||||
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;
|
||||
base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR);
|
||||
} else {
|
||||
bit = irq - AU1000_INTC0_INT_BASE;
|
||||
chip = &au1x_ic0_chip;
|
||||
base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR);
|
||||
}
|
||||
|
||||
if (bit > 31)
|
||||
return -EINVAL;
|
||||
|
||||
ret = 0;
|
||||
|
||||
switch (flow_type) { /* cfgregs 2:1:0 */
|
||||
case IRQ_TYPE_EDGE_RISING: /* 0:0:1 */
|
||||
__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 */
|
||||
__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 */
|
||||
__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 */
|
||||
__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 */
|
||||
__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 */
|
||||
__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;
|
||||
}
|
||||
__irq_set_chip_handler_name_locked(d->irq, chip, handler, name);
|
||||
|
||||
wmb();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void gpic_pin_set_idlewake(unsigned int gpio, int allow)
|
||||
{
|
||||
au1300_gpic_chgcfg(gpio, GPIC_CFG_IDLEWAKE,
|
||||
allow ? GPIC_CFG_IDLEWAKE : 0);
|
||||
}
|
||||
|
||||
static void au1300_gpic_mask(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_IDIS);
|
||||
wmb();
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
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;
|
||||
|
||||
/* 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 (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 (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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
452
arch/mips/alchemy/common/platform.c
Normal file
452
arch/mips/alchemy/common/platform.c
Normal file
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* Platform device support for Au1x00 SoCs.
|
||||
*
|
||||
* Copyright 2004, Matt Porter <mporter@kernel.crashing.org>
|
||||
*
|
||||
* (C) Copyright Embedded Alley Solutions, Inc 2005
|
||||
* Author: Pantelis Antoniou <pantelis@embeddedalley.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#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/slab.h>
|
||||
#include <linux/usb/ehci_pdriver.h>
|
||||
#include <linux/usb/ohci_pdriver.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>
|
||||
|
||||
#include <prom.h>
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#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,
|
||||
};
|
||||
|
||||
static void __init alchemy_setup_uarts(int ctype)
|
||||
{
|
||||
long uartclk;
|
||||
int s = sizeof(struct plat_serial8250_port);
|
||||
int c = alchemy_get_uarts(ctype);
|
||||
struct plat_serial8250_port *ports;
|
||||
struct clk *clk = clk_get(NULL, ALCHEMY_PERIPH_CLK);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
if (clk_prepare_enable(clk)) {
|
||||
clk_put(clk);
|
||||
return;
|
||||
}
|
||||
uartclk = clk_get_rate(clk);
|
||||
clk_put(clk);
|
||||
|
||||
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;
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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 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,
|
||||
};
|
||||
|
||||
/* Power on callback for the ohci platform driver */
|
||||
static int alchemy_ohci_power_on(struct platform_device *pdev)
|
||||
{
|
||||
int unit;
|
||||
|
||||
unit = (pdev->id == 1) ?
|
||||
ALCHEMY_USB_OHCI1 : ALCHEMY_USB_OHCI0;
|
||||
|
||||
return alchemy_usb_control(unit, 1);
|
||||
}
|
||||
|
||||
/* Power off/suspend callback for the ohci platform driver */
|
||||
static void alchemy_ohci_power_off(struct platform_device *pdev)
|
||||
{
|
||||
int unit;
|
||||
|
||||
unit = (pdev->id == 1) ?
|
||||
ALCHEMY_USB_OHCI1 : ALCHEMY_USB_OHCI0;
|
||||
|
||||
alchemy_usb_control(unit, 0);
|
||||
}
|
||||
|
||||
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 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 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 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;
|
||||
}
|
||||
|
||||
(*d)->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
(*d)->num_resources = 2;
|
||||
(*d)->resource = *r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
/* 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");
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 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)
|
||||
},
|
||||
[ALCHEMY_CPU_AU1500] = {
|
||||
MAC_RES(AU1500_MAC0_PHYS_ADDR,
|
||||
AU1500_MACEN_PHYS_ADDR,
|
||||
AU1500_MAC0_DMA_INT,
|
||||
AU1000_MACDMA0_PHYS_ADDR)
|
||||
},
|
||||
[ALCHEMY_CPU_AU1100] = {
|
||||
MAC_RES(AU1000_MAC0_PHYS_ADDR,
|
||||
AU1000_MACEN_PHYS_ADDR,
|
||||
AU1100_MAC0_DMA_INT,
|
||||
AU1000_MACDMA0_PHYS_ADDR)
|
||||
},
|
||||
[ALCHEMY_CPU_AU1550] = {
|
||||
MAC_RES(AU1000_MAC0_PHYS_ADDR,
|
||||
AU1000_MACEN_PHYS_ADDR,
|
||||
AU1550_MAC0_DMA_INT,
|
||||
AU1000_MACDMA0_PHYS_ADDR)
|
||||
},
|
||||
};
|
||||
|
||||
static struct au1000_eth_platform_data au1xxx_eth0_platform_data = {
|
||||
.phy1_search_mac0 = 1,
|
||||
};
|
||||
|
||||
static struct platform_device au1xxx_eth0_device = {
|
||||
.name = "au1000-eth",
|
||||
.id = 0,
|
||||
.num_resources = MAC_RES_COUNT,
|
||||
.dev.platform_data = &au1xxx_eth0_platform_data,
|
||||
};
|
||||
|
||||
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 au1000_eth_platform_data au1xxx_eth1_platform_data = {
|
||||
.phy1_search_mac0 = 1,
|
||||
};
|
||||
|
||||
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 (!(alchemy_rdsys(AU1000_SYS_PINFUNC) & 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)
|
||||
{
|
||||
int ctype = alchemy_get_cputype();
|
||||
|
||||
alchemy_setup_uarts(ctype);
|
||||
alchemy_setup_macs(ctype);
|
||||
alchemy_setup_usb(ctype);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
arch_initcall(au1xxx_platform_init);
|
132
arch/mips/alchemy/common/power.c
Normal file
132
arch/mips/alchemy/common/power.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* BRIEF MODULE DESCRIPTION
|
||||
* Au1xx0 Power Management routines.
|
||||
*
|
||||
* Copyright 2001, 2008 MontaVista Software Inc.
|
||||
* Author: MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* Some of the routines are right out of init/main.c, whose
|
||||
* copyrights apply here.
|
||||
*
|
||||
* 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/pm.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
|
||||
/*
|
||||
* We need to save/restore a bunch of core registers that are
|
||||
* either volatile or reset to some state across a processor sleep.
|
||||
* If reading a register doesn't provide a proper result for a
|
||||
* later restore, we have to provide a function for loading that
|
||||
* register and save a copy.
|
||||
*
|
||||
* We only have to save/restore registers that aren't otherwise
|
||||
* done as part of a driver pm_* function.
|
||||
*/
|
||||
static unsigned int sleep_sys_clocks[5];
|
||||
static unsigned int sleep_sys_pinfunc;
|
||||
static unsigned int sleep_static_memctlr[4][3];
|
||||
|
||||
|
||||
static void save_core_regs(void)
|
||||
{
|
||||
/* Clocks and PLLs. */
|
||||
sleep_sys_clocks[0] = alchemy_rdsys(AU1000_SYS_FREQCTRL0);
|
||||
sleep_sys_clocks[1] = alchemy_rdsys(AU1000_SYS_FREQCTRL1);
|
||||
sleep_sys_clocks[2] = alchemy_rdsys(AU1000_SYS_CLKSRC);
|
||||
sleep_sys_clocks[3] = alchemy_rdsys(AU1000_SYS_CPUPLL);
|
||||
sleep_sys_clocks[4] = alchemy_rdsys(AU1000_SYS_AUXPLL);
|
||||
|
||||
/* pin mux config */
|
||||
sleep_sys_pinfunc = alchemy_rdsys(AU1000_SYS_PINFUNC);
|
||||
|
||||
/* Save the static memory controller configuration. */
|
||||
sleep_static_memctlr[0][0] = alchemy_rdsmem(AU1000_MEM_STCFG0);
|
||||
sleep_static_memctlr[0][1] = alchemy_rdsmem(AU1000_MEM_STTIME0);
|
||||
sleep_static_memctlr[0][2] = alchemy_rdsmem(AU1000_MEM_STADDR0);
|
||||
sleep_static_memctlr[1][0] = alchemy_rdsmem(AU1000_MEM_STCFG1);
|
||||
sleep_static_memctlr[1][1] = alchemy_rdsmem(AU1000_MEM_STTIME1);
|
||||
sleep_static_memctlr[1][2] = alchemy_rdsmem(AU1000_MEM_STADDR1);
|
||||
sleep_static_memctlr[2][0] = alchemy_rdsmem(AU1000_MEM_STCFG2);
|
||||
sleep_static_memctlr[2][1] = alchemy_rdsmem(AU1000_MEM_STTIME2);
|
||||
sleep_static_memctlr[2][2] = alchemy_rdsmem(AU1000_MEM_STADDR2);
|
||||
sleep_static_memctlr[3][0] = alchemy_rdsmem(AU1000_MEM_STCFG3);
|
||||
sleep_static_memctlr[3][1] = alchemy_rdsmem(AU1000_MEM_STTIME3);
|
||||
sleep_static_memctlr[3][2] = alchemy_rdsmem(AU1000_MEM_STADDR3);
|
||||
}
|
||||
|
||||
static void restore_core_regs(void)
|
||||
{
|
||||
/* restore clock configuration. Writing CPUPLL last will
|
||||
* stall a bit and stabilize other clocks (unless this is
|
||||
* one of those Au1000 with a write-only PLL, where we dont
|
||||
* have a valid value)
|
||||
*/
|
||||
alchemy_wrsys(sleep_sys_clocks[0], AU1000_SYS_FREQCTRL0);
|
||||
alchemy_wrsys(sleep_sys_clocks[1], AU1000_SYS_FREQCTRL1);
|
||||
alchemy_wrsys(sleep_sys_clocks[2], AU1000_SYS_CLKSRC);
|
||||
alchemy_wrsys(sleep_sys_clocks[4], AU1000_SYS_AUXPLL);
|
||||
if (!au1xxx_cpu_has_pll_wo())
|
||||
alchemy_wrsys(sleep_sys_clocks[3], AU1000_SYS_CPUPLL);
|
||||
|
||||
alchemy_wrsys(sleep_sys_pinfunc, AU1000_SYS_PINFUNC);
|
||||
|
||||
/* Restore the static memory controller configuration. */
|
||||
alchemy_wrsmem(sleep_static_memctlr[0][0], AU1000_MEM_STCFG0);
|
||||
alchemy_wrsmem(sleep_static_memctlr[0][1], AU1000_MEM_STTIME0);
|
||||
alchemy_wrsmem(sleep_static_memctlr[0][2], AU1000_MEM_STADDR0);
|
||||
alchemy_wrsmem(sleep_static_memctlr[1][0], AU1000_MEM_STCFG1);
|
||||
alchemy_wrsmem(sleep_static_memctlr[1][1], AU1000_MEM_STTIME1);
|
||||
alchemy_wrsmem(sleep_static_memctlr[1][2], AU1000_MEM_STADDR1);
|
||||
alchemy_wrsmem(sleep_static_memctlr[2][0], AU1000_MEM_STCFG2);
|
||||
alchemy_wrsmem(sleep_static_memctlr[2][1], AU1000_MEM_STTIME2);
|
||||
alchemy_wrsmem(sleep_static_memctlr[2][2], AU1000_MEM_STADDR2);
|
||||
alchemy_wrsmem(sleep_static_memctlr[3][0], AU1000_MEM_STCFG3);
|
||||
alchemy_wrsmem(sleep_static_memctlr[3][1], AU1000_MEM_STTIME3);
|
||||
alchemy_wrsmem(sleep_static_memctlr[3][2], AU1000_MEM_STADDR3);
|
||||
}
|
||||
|
||||
void au_sleep(void)
|
||||
{
|
||||
save_core_regs();
|
||||
|
||||
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();
|
||||
}
|
129
arch/mips/alchemy/common/prom.c
Normal file
129
arch/mips/alchemy/common/prom.c
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
*
|
||||
* BRIEF MODULE DESCRIPTION
|
||||
* PROM library initialisation code, supports YAMON and U-Boot.
|
||||
*
|
||||
* Copyright 2000-2001, 2006, 2008 MontaVista Software Inc.
|
||||
* Author: MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* This file was derived from Carsten Langgaard's
|
||||
* arch/mips/mips-boards/xx files.
|
||||
*
|
||||
* Carsten Langgaard, carstenl@mips.com
|
||||
* Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <asm/bootinfo.h>
|
||||
|
||||
int prom_argc;
|
||||
char **prom_argv;
|
||||
char **prom_envp;
|
||||
|
||||
void __init prom_init_cmdline(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
char *prom_getenv(char *envname)
|
||||
{
|
||||
/*
|
||||
* Return a pointer to the given environment variable.
|
||||
* YAMON uses "name", "value" pairs, while U-Boot uses "name=value".
|
||||
*/
|
||||
|
||||
char **env = prom_envp;
|
||||
int i = strlen(envname);
|
||||
int yamon = (*env && strchr(*env, '=') == NULL);
|
||||
|
||||
while (*env) {
|
||||
if (yamon) {
|
||||
if (strcmp(envname, *env++) == 0)
|
||||
return *env;
|
||||
} else if (strncmp(envname, *env, i) == 0 && (*env)[i] == '=')
|
||||
return *env + i + 1;
|
||||
env++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline unsigned char str2hexnum(unsigned char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
|
||||
return 0; /* foo */
|
||||
}
|
||||
|
||||
static inline void str2eaddr(unsigned char *ea, unsigned char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
unsigned char num;
|
||||
|
||||
if ((*str == '.') || (*str == ':'))
|
||||
str++;
|
||||
num = str2hexnum(*str++) << 4;
|
||||
num |= str2hexnum(*str++);
|
||||
ea[i] = num;
|
||||
}
|
||||
}
|
||||
|
||||
int __init prom_get_ethernet_addr(char *ethernet_addr)
|
||||
{
|
||||
char *ethaddr_str;
|
||||
|
||||
/* Check the environment variables first */
|
||||
ethaddr_str = prom_getenv("ethaddr");
|
||||
if (!ethaddr_str) {
|
||||
/* Check command line */
|
||||
ethaddr_str = strstr(arcs_cmdline, "ethaddr=");
|
||||
if (!ethaddr_str)
|
||||
return -1;
|
||||
|
||||
ethaddr_str += strlen("ethaddr=");
|
||||
}
|
||||
|
||||
str2eaddr(ethernet_addr, ethaddr_str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init prom_free_prom_memory(void)
|
||||
{
|
||||
}
|
92
arch/mips/alchemy/common/setup.c
Normal file
92
arch/mips/alchemy/common/setup.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2000, 2007-2008 MontaVista Software Inc.
|
||||
* Author: MontaVista Software, Inc. <source@mvista.com
|
||||
*
|
||||
* Updates to 2.6, Pete Popov, Embedded Alley Solutions, Inc.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
#include <asm/dma-coherence.h>
|
||||
#include <asm/mipsregs.h>
|
||||
|
||||
#include <au1000.h>
|
||||
|
||||
extern void __init board_setup(void);
|
||||
extern void set_cpuspec(void);
|
||||
|
||||
void __init plat_mem_setup(void)
|
||||
{
|
||||
if (au1xxx_cpu_needs_config_od())
|
||||
/* Various early Au1xx0 errata corrected by this */
|
||||
set_c0_config(1 << 19); /* Set Config[OD] */
|
||||
else
|
||||
/* 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;
|
||||
ioport_resource.end = IOPORT_RESOURCE_END;
|
||||
iomem_resource.start = IOMEM_RESOURCE_START;
|
||||
iomem_resource.end = IOMEM_RESOURCE_END;
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
EXPORT_SYMBOL(__fixup_bigphys_addr);
|
||||
#endif
|
270
arch/mips/alchemy/common/sleeper.S
Normal file
270
arch/mips/alchemy/common/sleeper.S
Normal file
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright 2002 Embedded Edge, LLC
|
||||
* Author: dan@embeddededge.com
|
||||
*
|
||||
* Sleep helper for Au1xxx sleep mode.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <asm/asm.h>
|
||||
#include <asm/mipsregs.h>
|
||||
#include <asm/regdef.h>
|
||||
#include <asm/stackframe.h>
|
||||
|
||||
.extern __flush_cache_all
|
||||
|
||||
.text
|
||||
.set noreorder
|
||||
.set noat
|
||||
.align 5
|
||||
|
||||
|
||||
/* preparatory stuff */
|
||||
.macro SETUP_SLEEP
|
||||
subu sp, PT_SIZE
|
||||
sw $1, PT_R1(sp)
|
||||
sw $2, PT_R2(sp)
|
||||
sw $3, PT_R3(sp)
|
||||
sw $4, PT_R4(sp)
|
||||
sw $5, PT_R5(sp)
|
||||
sw $6, PT_R6(sp)
|
||||
sw $7, PT_R7(sp)
|
||||
sw $16, PT_R16(sp)
|
||||
sw $17, PT_R17(sp)
|
||||
sw $18, PT_R18(sp)
|
||||
sw $19, PT_R19(sp)
|
||||
sw $20, PT_R20(sp)
|
||||
sw $21, PT_R21(sp)
|
||||
sw $22, PT_R22(sp)
|
||||
sw $23, PT_R23(sp)
|
||||
sw $26, PT_R26(sp)
|
||||
sw $27, PT_R27(sp)
|
||||
sw $28, PT_R28(sp)
|
||||
sw $30, PT_R30(sp)
|
||||
sw $31, PT_R31(sp)
|
||||
mfc0 k0, CP0_STATUS
|
||||
sw k0, 0x20(sp)
|
||||
mfc0 k0, CP0_CONTEXT
|
||||
sw k0, 0x1c(sp)
|
||||
mfc0 k0, CP0_PAGEMASK
|
||||
sw k0, 0x18(sp)
|
||||
mfc0 k0, CP0_CONFIG
|
||||
sw k0, 0x14(sp)
|
||||
|
||||
/* flush caches to make sure context is in memory */
|
||||
la t1, __flush_cache_all
|
||||
lw t0, 0(t1)
|
||||
jalr t0
|
||||
nop
|
||||
|
||||
/* Now set up the scratch registers so the boot rom will
|
||||
* return to this point upon wakeup.
|
||||
* sys_scratch0 : SP
|
||||
* sys_scratch1 : RA
|
||||
*/
|
||||
lui t3, 0xb190 /* sys_xxx */
|
||||
sw sp, 0x0018(t3)
|
||||
la k0, alchemy_sleep_wakeup /* resume path */
|
||||
sw k0, 0x001c(t3)
|
||||
.endm
|
||||
|
||||
.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 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, 0x001c(a0) /* Precharge */
|
||||
sync
|
||||
sw zero, 0x0020(a0) /* Auto Refresh */
|
||||
sync
|
||||
sw zero, 0x0030(a0) /* Sleep */
|
||||
sync
|
||||
|
||||
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 */
|
||||
and t2, t1, t0
|
||||
beq t2, zero, 2b
|
||||
nop
|
||||
|
||||
/* 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 */
|
||||
sync
|
||||
|
||||
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
|
||||
|
||||
/* disable ODT */
|
||||
li t0, 0x03010000
|
||||
sw t0, 0x08d8(a0) /* mem_sdcmd0 */
|
||||
sw t0, 0x08dc(a0) /* mem_sdcmd1 */
|
||||
sync
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
LEAF(alchemy_sleep_wakeup)
|
||||
lw k0, 0x20(sp)
|
||||
mtc0 k0, CP0_STATUS
|
||||
lw k0, 0x1c(sp)
|
||||
mtc0 k0, CP0_CONTEXT
|
||||
lw k0, 0x18(sp)
|
||||
mtc0 k0, CP0_PAGEMASK
|
||||
lw k0, 0x14(sp)
|
||||
mtc0 k0, CP0_CONFIG
|
||||
|
||||
/* We need to catch the early Alchemy SOCs with
|
||||
* the write-only Config[OD] bit and set it back to one...
|
||||
*/
|
||||
jal au1x00_fixup_config_od
|
||||
nop
|
||||
lw $1, PT_R1(sp)
|
||||
lw $2, PT_R2(sp)
|
||||
lw $3, PT_R3(sp)
|
||||
lw $4, PT_R4(sp)
|
||||
lw $5, PT_R5(sp)
|
||||
lw $6, PT_R6(sp)
|
||||
lw $7, PT_R7(sp)
|
||||
lw $16, PT_R16(sp)
|
||||
lw $17, PT_R17(sp)
|
||||
lw $18, PT_R18(sp)
|
||||
lw $19, PT_R19(sp)
|
||||
lw $20, PT_R20(sp)
|
||||
lw $21, PT_R21(sp)
|
||||
lw $22, PT_R22(sp)
|
||||
lw $23, PT_R23(sp)
|
||||
lw $26, PT_R26(sp)
|
||||
lw $27, PT_R27(sp)
|
||||
lw $28, PT_R28(sp)
|
||||
lw $30, PT_R30(sp)
|
||||
lw $31, PT_R31(sp)
|
||||
jr ra
|
||||
addiu sp, PT_SIZE
|
||||
END(alchemy_sleep_wakeup)
|
176
arch/mips/alchemy/common/time.c
Normal file
176
arch/mips/alchemy/common/time.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (C) 2008-2009 Manuel Lauss <manuel.lauss@gmail.com>
|
||||
*
|
||||
* Previous incarnations were:
|
||||
* Copyright (C) 2001, 2006, 2008 MontaVista Software, <source@mvista.com>
|
||||
* Copied and modified Carsten Langgaard's time.c
|
||||
*
|
||||
* Carsten Langgaard, carstenl@mips.com
|
||||
* Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* ########################################################################
|
||||
*
|
||||
* This program is free software; you can distribute it and/or modify it
|
||||
* under the terms of the GNU General Public License (Version 2) as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
|
||||
*
|
||||
* ########################################################################
|
||||
*
|
||||
* Clocksource/event using the 32.768kHz-clocked Counter1 ('RTC' in the
|
||||
* databooks). Firmware/Board init code must enable the counters in the
|
||||
* counter control register, otherwise the CP0 counter clocksource/event
|
||||
* will be installed instead (and use of 'wait' instruction is prohibited).
|
||||
*/
|
||||
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clocksource.h>
|
||||
#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)
|
||||
|
||||
static cycle_t au1x_counter1_read(struct clocksource *cs)
|
||||
{
|
||||
return alchemy_rdsys(AU1000_SYS_RTCREAD);
|
||||
}
|
||||
|
||||
static struct clocksource au1x_counter1_clocksource = {
|
||||
.name = "alchemy-counter1",
|
||||
.read = au1x_counter1_read,
|
||||
.mask = CLOCKSOURCE_MASK(32),
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
.rating = 1500,
|
||||
};
|
||||
|
||||
static int au1x_rtcmatch2_set_next_event(unsigned long delta,
|
||||
struct clock_event_device *cd)
|
||||
{
|
||||
delta += alchemy_rdsys(AU1000_SYS_RTCREAD);
|
||||
/* wait for register access */
|
||||
while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M21)
|
||||
;
|
||||
alchemy_wrsys(delta, AU1000_SYS_RTCMATCH2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void au1x_rtcmatch2_set_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *cd)
|
||||
{
|
||||
}
|
||||
|
||||
static irqreturn_t au1x_rtcmatch2_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *cd = dev_id;
|
||||
cd->event_handler(cd);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct clock_event_device au1x_rtcmatch2_clockdev = {
|
||||
.name = "rtcmatch2",
|
||||
.features = CLOCK_EVT_FEAT_ONESHOT,
|
||||
.rating = 1500,
|
||||
.set_next_event = au1x_rtcmatch2_set_next_event,
|
||||
.set_mode = au1x_rtcmatch2_set_mode,
|
||||
.cpumask = cpu_all_mask,
|
||||
};
|
||||
|
||||
static struct irqaction au1x_rtcmatch2_irqaction = {
|
||||
.handler = au1x_rtcmatch2_irq,
|
||||
.flags = IRQF_TIMER,
|
||||
.name = "timer",
|
||||
.dev_id = &au1x_rtcmatch2_clockdev,
|
||||
};
|
||||
|
||||
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
|
||||
* no means a definite guarantee that the counters actually work
|
||||
* (the 32S bit seems to be stuck set to 1 once a single clock-
|
||||
* edge is detected, hence the timeouts).
|
||||
*/
|
||||
if (CNTR_OK != (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & CNTR_OK))
|
||||
goto cntr_err;
|
||||
|
||||
/*
|
||||
* setup counter 1 (RTC) to tick at full speed
|
||||
*/
|
||||
t = 0xffffff;
|
||||
while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_T1S) && --t)
|
||||
asm volatile ("nop");
|
||||
if (!t)
|
||||
goto cntr_err;
|
||||
|
||||
alchemy_wrsys(0, AU1000_SYS_RTCTRIM); /* 32.768 kHz */
|
||||
|
||||
t = 0xffffff;
|
||||
while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C1S) && --t)
|
||||
asm volatile ("nop");
|
||||
if (!t)
|
||||
goto cntr_err;
|
||||
alchemy_wrsys(0, AU1000_SYS_RTCWRITE);
|
||||
|
||||
t = 0xffffff;
|
||||
while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C1S) && --t)
|
||||
asm volatile ("nop");
|
||||
if (!t)
|
||||
goto cntr_err;
|
||||
|
||||
/* register counter1 clocksource and event device */
|
||||
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(9, cd); /* ~0.28ms */
|
||||
clockevents_register_device(cd);
|
||||
setup_irq(m2int, &au1x_rtcmatch2_irqaction);
|
||||
|
||||
printk(KERN_INFO "Alchemy clocksource installed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
cntr_err:
|
||||
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 */
|
||||
}
|
626
arch/mips/alchemy/common/usb.c
Normal file
626
arch/mips/alchemy/common/usb.c
Normal file
|
@ -0,0 +1,626 @@
|
|||
/*
|
||||
* 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/clk.h>
|
||||
#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 int au1000_usb_init(unsigned long rb, int reg)
|
||||
{
|
||||
void __iomem *base = (void __iomem *)KSEG1ADDR(rb + reg);
|
||||
unsigned long r = __raw_readl(base);
|
||||
struct clk *c;
|
||||
|
||||
/* 48MHz check. Don't init if no one can provide it */
|
||||
c = clk_get(NULL, "usbh_clk");
|
||||
if (IS_ERR(c))
|
||||
return -ENODEV;
|
||||
if (clk_round_rate(c, 48000000) != 48000000) {
|
||||
clk_put(c);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (clk_set_rate(c, 48000000)) {
|
||||
clk_put(c);
|
||||
return -ENODEV;
|
||||
}
|
||||
clk_put(c);
|
||||
|
||||
#if defined(__BIG_ENDIAN)
|
||||
r |= USBHEN_BE;
|
||||
#endif
|
||||
r |= USBHEN_C;
|
||||
|
||||
__raw_writel(r, base);
|
||||
wmb();
|
||||
udelay(1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
struct clk *c = clk_get(NULL, "usbh_clk");
|
||||
|
||||
if (IS_ERR(c))
|
||||
return;
|
||||
|
||||
if (enable) {
|
||||
if (clk_prepare_enable(c))
|
||||
goto out;
|
||||
|
||||
__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();
|
||||
clk_disable_unprepare(c);
|
||||
}
|
||||
out:
|
||||
clk_put(c);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (alchemy_get_cputype()) {
|
||||
case ALCHEMY_CPU_AU1000:
|
||||
case ALCHEMY_CPU_AU1500:
|
||||
case ALCHEMY_CPU_AU1100:
|
||||
ret = au1000_usb_init(AU1000_USB_OHCI_PHYS_ADDR,
|
||||
AU1000_OHCICFG);
|
||||
break;
|
||||
case ALCHEMY_CPU_AU1550:
|
||||
ret = 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;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
register_syscore_ops(&alchemy_usb_pm_ops);
|
||||
|
||||
return ret;
|
||||
}
|
||||
arch_initcall(alchemy_usb_init);
|
84
arch/mips/alchemy/common/vss.c
Normal file
84
arch/mips/alchemy/common/vss.c
Normal file
|
@ -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);
|
6
arch/mips/alchemy/devboards/Makefile
Normal file
6
arch/mips/alchemy/devboards/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Alchemy Develboards
|
||||
#
|
||||
|
||||
obj-y += bcsr.o platform.o db1000.o db1200.o db1300.o db1550.o db1xxx.o
|
||||
obj-$(CONFIG_PM) += pm.o
|
143
arch/mips/alchemy/devboards/bcsr.c
Normal file
143
arch/mips/alchemy/devboards/bcsr.c
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* bcsr.h -- Db1xxx/Pb1xxx Devboard CPLD registers ("BCSR") abstraction.
|
||||
*
|
||||
* All Alchemy development boards (except, of course, the weird PB1000)
|
||||
* have a few registers in a CPLD with standardised layout; they mostly
|
||||
* only differ in base address.
|
||||
* All registers are 16bits wide with 32bit spacing.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/irq.h>
|
||||
#include <asm/addrspace.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mach-db1x00/bcsr.h>
|
||||
|
||||
static struct bcsr_reg {
|
||||
void __iomem *raddr;
|
||||
spinlock_t lock;
|
||||
} bcsr_regs[BCSR_CNT];
|
||||
|
||||
static void __iomem *bcsr_virt; /* KSEG1 addr of BCSR base */
|
||||
static int bcsr_csc_base; /* linux-irq of first cascaded irq */
|
||||
|
||||
void __init bcsr_init(unsigned long bcsr1_phys, unsigned long bcsr2_phys)
|
||||
{
|
||||
int i;
|
||||
|
||||
bcsr1_phys = KSEG1ADDR(CPHYSADDR(bcsr1_phys));
|
||||
bcsr2_phys = KSEG1ADDR(CPHYSADDR(bcsr2_phys));
|
||||
|
||||
bcsr_virt = (void __iomem *)bcsr1_phys;
|
||||
|
||||
for (i = 0; i < BCSR_CNT; i++) {
|
||||
if (i >= BCSR_HEXLEDS)
|
||||
bcsr_regs[i].raddr = (void __iomem *)bcsr2_phys +
|
||||
(0x04 * (i - BCSR_HEXLEDS));
|
||||
else
|
||||
bcsr_regs[i].raddr = (void __iomem *)bcsr1_phys +
|
||||
(0x04 * i);
|
||||
|
||||
spin_lock_init(&bcsr_regs[i].lock);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short bcsr_read(enum bcsr_id reg)
|
||||
{
|
||||
unsigned short r;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&bcsr_regs[reg].lock, flags);
|
||||
r = __raw_readw(bcsr_regs[reg].raddr);
|
||||
spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags);
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcsr_read);
|
||||
|
||||
void bcsr_write(enum bcsr_id reg, unsigned short val)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&bcsr_regs[reg].lock, flags);
|
||||
__raw_writew(val, bcsr_regs[reg].raddr);
|
||||
wmb();
|
||||
spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcsr_write);
|
||||
|
||||
void bcsr_mod(enum bcsr_id reg, unsigned short clr, unsigned short set)
|
||||
{
|
||||
unsigned short r;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&bcsr_regs[reg].lock, flags);
|
||||
r = __raw_readw(bcsr_regs[reg].raddr);
|
||||
r &= ~clr;
|
||||
r |= set;
|
||||
__raw_writew(r, bcsr_regs[reg].raddr);
|
||||
wmb();
|
||||
spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcsr_mod);
|
||||
|
||||
/*
|
||||
* DB1200/PB1200 CPLD IRQ muxer
|
||||
*/
|
||||
static void bcsr_csc_handler(unsigned int irq, struct irq_desc *d)
|
||||
{
|
||||
unsigned short bisr = __raw_readw(bcsr_virt + BCSR_REG_INTSTAT);
|
||||
|
||||
disable_irq_nosync(irq);
|
||||
generic_handle_irq(bcsr_csc_base + __ffs(bisr));
|
||||
enable_irq(irq);
|
||||
}
|
||||
|
||||
static void bcsr_irq_mask(struct irq_data *d)
|
||||
{
|
||||
unsigned short v = 1 << (d->irq - bcsr_csc_base);
|
||||
__raw_writew(v, bcsr_virt + BCSR_REG_MASKCLR);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static void bcsr_irq_maskack(struct irq_data *d)
|
||||
{
|
||||
unsigned short v = 1 << (d->irq - bcsr_csc_base);
|
||||
__raw_writew(v, bcsr_virt + BCSR_REG_MASKCLR);
|
||||
__raw_writew(v, bcsr_virt + BCSR_REG_INTSTAT); /* ack */
|
||||
wmb();
|
||||
}
|
||||
|
||||
static void bcsr_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
unsigned short v = 1 << (d->irq - bcsr_csc_base);
|
||||
__raw_writew(v, bcsr_virt + BCSR_REG_MASKSET);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static struct irq_chip bcsr_irq_type = {
|
||||
.name = "CPLD",
|
||||
.irq_mask = bcsr_irq_mask,
|
||||
.irq_mask_ack = bcsr_irq_maskack,
|
||||
.irq_unmask = bcsr_irq_unmask,
|
||||
};
|
||||
|
||||
void __init bcsr_init_irq(int csc_start, int csc_end, int hook_irq)
|
||||
{
|
||||
unsigned int irq;
|
||||
|
||||
/* mask & enable & ack all */
|
||||
__raw_writew(0xffff, bcsr_virt + BCSR_REG_MASKCLR);
|
||||
__raw_writew(0xffff, bcsr_virt + BCSR_REG_INTSET);
|
||||
__raw_writew(0xffff, bcsr_virt + BCSR_REG_INTSTAT);
|
||||
wmb();
|
||||
|
||||
bcsr_csc_base = csc_start;
|
||||
|
||||
for (irq = csc_start; irq <= csc_end; irq++)
|
||||
irq_set_chip_and_handler_name(irq, &bcsr_irq_type,
|
||||
handle_level_irq, "level");
|
||||
|
||||
irq_set_chained_handler(hook_irq, bcsr_csc_handler);
|
||||
}
|
616
arch/mips/alchemy/devboards/db1000.c
Normal file
616
arch/mips/alchemy/devboards/db1000.c
Normal file
|
@ -0,0 +1,616 @@
|
|||
/*
|
||||
* DBAu1000/1500/1100 PBAu1100/1500 board support
|
||||
*
|
||||
* Copyright 2000, 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_gpio.h>
|
||||
#include <linux/spi/ads7846.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-au1x00/au1000_dma.h>
|
||||
#include <asm/mach-au1x00/au1100_mmc.h>
|
||||
#include <asm/mach-db1x00/bcsr.h>
|
||||
#include <asm/reboot.h>
|
||||
#include <prom.h>
|
||||
#include "platform.h"
|
||||
|
||||
#define F_SWAPPED (bcsr_read(BCSR_STATUS) & BCSR_STATUS_DB1000_SWAPBOOT)
|
||||
|
||||
const char *get_system_type(void);
|
||||
|
||||
int __init db1000_board_setup(void)
|
||||
{
|
||||
/* initialize board register space */
|
||||
bcsr_init(DB1000_BCSR_PHYS_ADDR,
|
||||
DB1000_BCSR_PHYS_ADDR + DB1000_BCSR_HEXLED_OFS);
|
||||
|
||||
switch (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI))) {
|
||||
case BCSR_WHOAMI_DB1000:
|
||||
case BCSR_WHOAMI_DB1500:
|
||||
case BCSR_WHOAMI_DB1100:
|
||||
case BCSR_WHOAMI_PB1500:
|
||||
case BCSR_WHOAMI_PB1500R2:
|
||||
case BCSR_WHOAMI_PB1100:
|
||||
pr_info("AMD Alchemy %s Board\n", get_system_type());
|
||||
return 0;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int db1500_map_pci_irq(const struct pci_dev *d, u8 slot, u8 pin)
|
||||
{
|
||||
if ((slot < 12) || (slot > 13) || pin == 0)
|
||||
return -1;
|
||||
if (slot == 12)
|
||||
return (pin == 1) ? AU1500_PCI_INTA : 0xff;
|
||||
if (slot == 13) {
|
||||
switch (pin) {
|
||||
case 1: return AU1500_PCI_INTA;
|
||||
case 2: return AU1500_PCI_INTB;
|
||||
case 3: return AU1500_PCI_INTC;
|
||||
case 4: return AU1500_PCI_INTD;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct resource alchemy_pci_host_res[] = {
|
||||
[0] = {
|
||||
.start = AU1500_PCI_PHYS_ADDR,
|
||||
.end = AU1500_PCI_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct alchemy_pci_platdata db1500_pci_pd = {
|
||||
.board_map_irq = db1500_map_pci_irq,
|
||||
};
|
||||
|
||||
static struct platform_device db1500_pci_host_dev = {
|
||||
.dev.platform_data = &db1500_pci_pd,
|
||||
.name = "alchemy-pci",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(alchemy_pci_host_res),
|
||||
.resource = alchemy_pci_host_res,
|
||||
};
|
||||
|
||||
int __init db1500_pci_setup(void)
|
||||
{
|
||||
return platform_device_register(&db1500_pci_host_dev);
|
||||
}
|
||||
|
||||
static struct resource au1100_lcd_resources[] = {
|
||||
[0] = {
|
||||
.start = AU1100_LCD_PHYS_ADDR,
|
||||
.end = AU1100_LCD_PHYS_ADDR + 0x800 - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1100_LCD_INT,
|
||||
.end = AU1100_LCD_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static u64 au1100_lcd_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device au1100_lcd_device = {
|
||||
.name = "au1100-lcd",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &au1100_lcd_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(au1100_lcd_resources),
|
||||
.resource = au1100_lcd_resources,
|
||||
};
|
||||
|
||||
static struct resource alchemy_ac97c_res[] = {
|
||||
[0] = {
|
||||
.start = AU1000_AC97_PHYS_ADDR,
|
||||
.end = AU1000_AC97_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = DMA_ID_AC97C_TX,
|
||||
.end = DMA_ID_AC97C_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[2] = {
|
||||
.start = DMA_ID_AC97C_RX,
|
||||
.end = DMA_ID_AC97C_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device alchemy_ac97c_dev = {
|
||||
.name = "alchemy-ac97c",
|
||||
.id = -1,
|
||||
.resource = alchemy_ac97c_res,
|
||||
.num_resources = ARRAY_SIZE(alchemy_ac97c_res),
|
||||
};
|
||||
|
||||
static struct platform_device alchemy_ac97c_dma_dev = {
|
||||
.name = "alchemy-pcm-dma",
|
||||
.id = 0,
|
||||
};
|
||||
|
||||
static struct platform_device db1x00_codec_dev = {
|
||||
.name = "ac97-codec",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
static struct platform_device db1x00_audio_dev = {
|
||||
.name = "db1000-audio",
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static irqreturn_t db1100_mmc_cd(int irq, void *ptr)
|
||||
{
|
||||
void (*mmc_cd)(struct mmc_host *, unsigned long);
|
||||
/* link against CONFIG_MMC=m */
|
||||
mmc_cd = symbol_get(mmc_detect_change);
|
||||
mmc_cd(ptr, msecs_to_jiffies(500));
|
||||
symbol_put(mmc_detect_change);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int db1100_mmc_cd_setup(void *mmc_host, int en)
|
||||
{
|
||||
int ret = 0, irq;
|
||||
|
||||
if (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)) == BCSR_WHOAMI_DB1100)
|
||||
irq = AU1100_GPIO19_INT;
|
||||
else
|
||||
irq = AU1100_GPIO14_INT; /* PB1100 SD0 CD# */
|
||||
|
||||
if (en) {
|
||||
irq_set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
|
||||
ret = request_irq(irq, db1100_mmc_cd, 0,
|
||||
"sd0_cd", mmc_host);
|
||||
} else
|
||||
free_irq(irq, mmc_host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int db1100_mmc1_cd_setup(void *mmc_host, int en)
|
||||
{
|
||||
int ret = 0, irq;
|
||||
|
||||
if (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)) == BCSR_WHOAMI_DB1100)
|
||||
irq = AU1100_GPIO20_INT;
|
||||
else
|
||||
irq = AU1100_GPIO15_INT; /* PB1100 SD1 CD# */
|
||||
|
||||
if (en) {
|
||||
irq_set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
|
||||
ret = request_irq(irq, db1100_mmc_cd, 0,
|
||||
"sd1_cd", mmc_host);
|
||||
} else
|
||||
free_irq(irq, mmc_host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int db1100_mmc_card_readonly(void *mmc_host)
|
||||
{
|
||||
/* testing suggests that this bit is inverted */
|
||||
return (bcsr_read(BCSR_STATUS) & BCSR_STATUS_SD0WP) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int db1100_mmc_card_inserted(void *mmc_host)
|
||||
{
|
||||
return !alchemy_gpio_get_value(19);
|
||||
}
|
||||
|
||||
static void db1100_mmc_set_power(void *mmc_host, int state)
|
||||
{
|
||||
int bit;
|
||||
|
||||
if (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)) == BCSR_WHOAMI_DB1100)
|
||||
bit = BCSR_BOARD_SD0PWR;
|
||||
else
|
||||
bit = BCSR_BOARD_PB1100_SD0PWR;
|
||||
|
||||
if (state) {
|
||||
bcsr_mod(BCSR_BOARD, 0, bit);
|
||||
msleep(400); /* stabilization time */
|
||||
} else
|
||||
bcsr_mod(BCSR_BOARD, bit, 0);
|
||||
}
|
||||
|
||||
static void db1100_mmcled_set(struct led_classdev *led, enum led_brightness b)
|
||||
{
|
||||
if (b != LED_OFF)
|
||||
bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED0, 0);
|
||||
else
|
||||
bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED0);
|
||||
}
|
||||
|
||||
static struct led_classdev db1100_mmc_led = {
|
||||
.brightness_set = db1100_mmcled_set,
|
||||
};
|
||||
|
||||
static int db1100_mmc1_card_readonly(void *mmc_host)
|
||||
{
|
||||
return (bcsr_read(BCSR_BOARD) & BCSR_BOARD_SD1WP) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int db1100_mmc1_card_inserted(void *mmc_host)
|
||||
{
|
||||
return !alchemy_gpio_get_value(20);
|
||||
}
|
||||
|
||||
static void db1100_mmc1_set_power(void *mmc_host, int state)
|
||||
{
|
||||
int bit;
|
||||
|
||||
if (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)) == BCSR_WHOAMI_DB1100)
|
||||
bit = BCSR_BOARD_SD1PWR;
|
||||
else
|
||||
bit = BCSR_BOARD_PB1100_SD1PWR;
|
||||
|
||||
if (state) {
|
||||
bcsr_mod(BCSR_BOARD, 0, bit);
|
||||
msleep(400); /* stabilization time */
|
||||
} else
|
||||
bcsr_mod(BCSR_BOARD, bit, 0);
|
||||
}
|
||||
|
||||
static void db1100_mmc1led_set(struct led_classdev *led, enum led_brightness b)
|
||||
{
|
||||
if (b != LED_OFF)
|
||||
bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED1, 0);
|
||||
else
|
||||
bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED1);
|
||||
}
|
||||
|
||||
static struct led_classdev db1100_mmc1_led = {
|
||||
.brightness_set = db1100_mmc1led_set,
|
||||
};
|
||||
|
||||
static struct au1xmmc_platform_data db1100_mmc_platdata[2] = {
|
||||
[0] = {
|
||||
.cd_setup = db1100_mmc_cd_setup,
|
||||
.set_power = db1100_mmc_set_power,
|
||||
.card_inserted = db1100_mmc_card_inserted,
|
||||
.card_readonly = db1100_mmc_card_readonly,
|
||||
.led = &db1100_mmc_led,
|
||||
},
|
||||
[1] = {
|
||||
.cd_setup = db1100_mmc1_cd_setup,
|
||||
.set_power = db1100_mmc1_set_power,
|
||||
.card_inserted = db1100_mmc1_card_inserted,
|
||||
.card_readonly = db1100_mmc1_card_readonly,
|
||||
.led = &db1100_mmc1_led,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource au1100_mmc0_resources[] = {
|
||||
[0] = {
|
||||
.start = AU1100_SD0_PHYS_ADDR,
|
||||
.end = AU1100_SD0_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1100_SD_INT,
|
||||
.end = AU1100_SD_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = DMA_ID_SD0_TX,
|
||||
.end = DMA_ID_SD0_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = DMA_ID_SD0_RX,
|
||||
.end = DMA_ID_SD0_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
}
|
||||
};
|
||||
|
||||
static u64 au1xxx_mmc_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device db1100_mmc0_dev = {
|
||||
.name = "au1xxx-mmc",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &au1xxx_mmc_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
.platform_data = &db1100_mmc_platdata[0],
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(au1100_mmc0_resources),
|
||||
.resource = au1100_mmc0_resources,
|
||||
};
|
||||
|
||||
static struct resource au1100_mmc1_res[] = {
|
||||
[0] = {
|
||||
.start = AU1100_SD1_PHYS_ADDR,
|
||||
.end = AU1100_SD1_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1100_SD_INT,
|
||||
.end = AU1100_SD_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = DMA_ID_SD1_TX,
|
||||
.end = DMA_ID_SD1_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = DMA_ID_SD1_RX,
|
||||
.end = DMA_ID_SD1_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device db1100_mmc1_dev = {
|
||||
.name = "au1xxx-mmc",
|
||||
.id = 1,
|
||||
.dev = {
|
||||
.dma_mask = &au1xxx_mmc_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
.platform_data = &db1100_mmc_platdata[1],
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(au1100_mmc1_res),
|
||||
.resource = au1100_mmc1_res,
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void db1000_irda_set_phy_mode(int mode)
|
||||
{
|
||||
unsigned short mask = BCSR_RESETS_IRDA_MODE_MASK | BCSR_RESETS_FIR_SEL;
|
||||
|
||||
switch (mode) {
|
||||
case AU1000_IRDA_PHY_MODE_OFF:
|
||||
bcsr_mod(BCSR_RESETS, mask, BCSR_RESETS_IRDA_MODE_OFF);
|
||||
break;
|
||||
case AU1000_IRDA_PHY_MODE_SIR:
|
||||
bcsr_mod(BCSR_RESETS, mask, BCSR_RESETS_IRDA_MODE_FULL);
|
||||
break;
|
||||
case AU1000_IRDA_PHY_MODE_FIR:
|
||||
bcsr_mod(BCSR_RESETS, mask, BCSR_RESETS_IRDA_MODE_FULL |
|
||||
BCSR_RESETS_FIR_SEL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct au1k_irda_platform_data db1000_irda_platdata = {
|
||||
.set_phy_mode = db1000_irda_set_phy_mode,
|
||||
};
|
||||
|
||||
static struct resource au1000_irda_res[] = {
|
||||
[0] = {
|
||||
.start = AU1000_IRDA_PHYS_ADDR,
|
||||
.end = AU1000_IRDA_PHYS_ADDR + 0x0fff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1000_IRDA_TX_INT,
|
||||
.end = AU1000_IRDA_TX_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1000_IRDA_RX_INT,
|
||||
.end = AU1000_IRDA_RX_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1000_irda_dev = {
|
||||
.name = "au1000-irda",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &db1000_irda_platdata,
|
||||
},
|
||||
.resource = au1000_irda_res,
|
||||
.num_resources = ARRAY_SIZE(au1000_irda_res),
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static struct ads7846_platform_data db1100_touch_pd = {
|
||||
.model = 7846,
|
||||
.vref_mv = 3300,
|
||||
.gpio_pendown = 21,
|
||||
};
|
||||
|
||||
static struct spi_gpio_platform_data db1100_spictl_pd = {
|
||||
.sck = 209,
|
||||
.mosi = 208,
|
||||
.miso = 207,
|
||||
.num_chipselect = 1,
|
||||
};
|
||||
|
||||
static struct spi_board_info db1100_spi_info[] __initdata = {
|
||||
[0] = {
|
||||
.modalias = "ads7846",
|
||||
.max_speed_hz = 3250000,
|
||||
.bus_num = 0,
|
||||
.chip_select = 0,
|
||||
.mode = 0,
|
||||
.irq = AU1100_GPIO21_INT,
|
||||
.platform_data = &db1100_touch_pd,
|
||||
.controller_data = (void *)210, /* for spi_gpio: CS# GPIO210 */
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1100_spi_dev = {
|
||||
.name = "spi_gpio",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &db1100_spictl_pd,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static struct platform_device *db1x00_devs[] = {
|
||||
&db1x00_codec_dev,
|
||||
&alchemy_ac97c_dma_dev,
|
||||
&alchemy_ac97c_dev,
|
||||
&db1x00_audio_dev,
|
||||
};
|
||||
|
||||
static struct platform_device *db1000_devs[] = {
|
||||
&db1000_irda_dev,
|
||||
};
|
||||
|
||||
static struct platform_device *db1100_devs[] = {
|
||||
&au1100_lcd_device,
|
||||
&db1100_mmc0_dev,
|
||||
&db1100_mmc1_dev,
|
||||
&db1000_irda_dev,
|
||||
};
|
||||
|
||||
int __init db1000_dev_setup(void)
|
||||
{
|
||||
int board = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI));
|
||||
int c0, c1, d0, d1, s0, s1, flashsize = 32, twosocks = 1;
|
||||
unsigned long pfc;
|
||||
struct clk *c, *p;
|
||||
|
||||
if (board == BCSR_WHOAMI_DB1500) {
|
||||
c0 = AU1500_GPIO2_INT;
|
||||
c1 = AU1500_GPIO5_INT;
|
||||
d0 = AU1500_GPIO0_INT;
|
||||
d1 = AU1500_GPIO3_INT;
|
||||
s0 = AU1500_GPIO1_INT;
|
||||
s1 = AU1500_GPIO4_INT;
|
||||
} else if (board == BCSR_WHOAMI_DB1100) {
|
||||
c0 = AU1100_GPIO2_INT;
|
||||
c1 = AU1100_GPIO5_INT;
|
||||
d0 = AU1100_GPIO0_INT;
|
||||
d1 = AU1100_GPIO3_INT;
|
||||
s0 = AU1100_GPIO1_INT;
|
||||
s1 = AU1100_GPIO4_INT;
|
||||
|
||||
gpio_request(19, "sd0_cd");
|
||||
gpio_request(20, "sd1_cd");
|
||||
gpio_direction_input(19); /* sd0 cd# */
|
||||
gpio_direction_input(20); /* sd1 cd# */
|
||||
|
||||
/* spi_gpio on SSI0 pins */
|
||||
pfc = alchemy_rdsys(AU1000_SYS_PINFUNC);
|
||||
pfc |= (1 << 0); /* SSI0 pins as GPIOs */
|
||||
alchemy_wrsys(pfc, AU1000_SYS_PINFUNC);
|
||||
|
||||
spi_register_board_info(db1100_spi_info,
|
||||
ARRAY_SIZE(db1100_spi_info));
|
||||
|
||||
/* link LCD clock to AUXPLL */
|
||||
p = clk_get(NULL, "auxpll_clk");
|
||||
c = clk_get(NULL, "lcd_intclk");
|
||||
if (!IS_ERR(c) && !IS_ERR(p)) {
|
||||
clk_set_parent(c, p);
|
||||
clk_set_rate(c, clk_get_rate(p));
|
||||
}
|
||||
if (!IS_ERR(c))
|
||||
clk_put(c);
|
||||
if (!IS_ERR(p))
|
||||
clk_put(p);
|
||||
|
||||
platform_add_devices(db1100_devs, ARRAY_SIZE(db1100_devs));
|
||||
platform_device_register(&db1100_spi_dev);
|
||||
} else if (board == BCSR_WHOAMI_DB1000) {
|
||||
c0 = AU1000_GPIO2_INT;
|
||||
c1 = AU1000_GPIO5_INT;
|
||||
d0 = AU1000_GPIO0_INT;
|
||||
d1 = AU1000_GPIO3_INT;
|
||||
s0 = AU1000_GPIO1_INT;
|
||||
s1 = AU1000_GPIO4_INT;
|
||||
platform_add_devices(db1000_devs, ARRAY_SIZE(db1000_devs));
|
||||
} else if ((board == BCSR_WHOAMI_PB1500) ||
|
||||
(board == BCSR_WHOAMI_PB1500R2)) {
|
||||
c0 = AU1500_GPIO203_INT;
|
||||
d0 = AU1500_GPIO201_INT;
|
||||
s0 = AU1500_GPIO202_INT;
|
||||
twosocks = 0;
|
||||
flashsize = 64;
|
||||
/* RTC and daughtercard irqs */
|
||||
irq_set_irq_type(AU1500_GPIO204_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1500_GPIO205_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
/* EPSON S1D13806 0x1b000000
|
||||
* SRAM 1MB/2MB 0x1a000000
|
||||
* DS1693 RTC 0x0c000000
|
||||
*/
|
||||
} else if (board == BCSR_WHOAMI_PB1100) {
|
||||
c0 = AU1100_GPIO11_INT;
|
||||
d0 = AU1100_GPIO9_INT;
|
||||
s0 = AU1100_GPIO10_INT;
|
||||
twosocks = 0;
|
||||
flashsize = 64;
|
||||
/* pendown, rtc, daughtercard irqs */
|
||||
irq_set_irq_type(AU1100_GPIO8_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1100_GPIO12_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1100_GPIO13_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
/* EPSON S1D13806 0x1b000000
|
||||
* SRAM 1MB/2MB 0x1a000000
|
||||
* DiskOnChip 0x0d000000
|
||||
* DS1693 RTC 0x0c000000
|
||||
*/
|
||||
platform_add_devices(db1100_devs, ARRAY_SIZE(db1100_devs));
|
||||
} else
|
||||
return 0; /* unknown board, no further dev setup to do */
|
||||
|
||||
irq_set_irq_type(d0, IRQ_TYPE_EDGE_BOTH);
|
||||
irq_set_irq_type(c0, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(s0, IRQ_TYPE_LEVEL_LOW);
|
||||
|
||||
db1x_register_pcmcia_socket(
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR,
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x000400000 - 1,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x000010000 - 1,
|
||||
c0, d0, /*s0*/0, 0, 0);
|
||||
|
||||
if (twosocks) {
|
||||
irq_set_irq_type(d1, IRQ_TYPE_EDGE_BOTH);
|
||||
irq_set_irq_type(c1, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(s1, IRQ_TYPE_LEVEL_LOW);
|
||||
|
||||
db1x_register_pcmcia_socket(
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004000000,
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004400000 - 1,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004000000,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004400000 - 1,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x004000000,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x004010000 - 1,
|
||||
c1, d1, /*s1*/0, 0, 1);
|
||||
}
|
||||
|
||||
platform_add_devices(db1x00_devs, ARRAY_SIZE(db1x00_devs));
|
||||
db1x_register_norflash(flashsize << 20, 4 /* 32bit */, F_SWAPPED);
|
||||
return 0;
|
||||
}
|
961
arch/mips/alchemy/devboards/db1200.c
Normal file
961
arch/mips/alchemy/devboards/db1200.c
Normal file
|
@ -0,0 +1,961 @@
|
|||
/*
|
||||
* DBAu1200/PBAu1200 board platform device registration
|
||||
*
|
||||
* Copyright (C) 2008-2011 Manuel Lauss
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/serial_8250.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/flash.h>
|
||||
#include <linux/smc91x.h>
|
||||
#include <linux/ata_platform.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-au1x00/au1100_mmc.h>
|
||||
#include <asm/mach-au1x00/au1xxx_dbdma.h>
|
||||
#include <asm/mach-au1x00/au1xxx_psc.h>
|
||||
#include <asm/mach-au1x00/au1200fb.h>
|
||||
#include <asm/mach-au1x00/au1550_spi.h>
|
||||
#include <asm/mach-db1x00/bcsr.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#define BCSR_INT_IDE 0x0001
|
||||
#define BCSR_INT_ETH 0x0002
|
||||
#define BCSR_INT_PC0 0x0004
|
||||
#define BCSR_INT_PC0STSCHG 0x0008
|
||||
#define BCSR_INT_PC1 0x0010
|
||||
#define BCSR_INT_PC1STSCHG 0x0020
|
||||
#define BCSR_INT_DC 0x0040
|
||||
#define BCSR_INT_FLASHBUSY 0x0080
|
||||
#define BCSR_INT_PC0INSERT 0x0100
|
||||
#define BCSR_INT_PC0EJECT 0x0200
|
||||
#define BCSR_INT_PC1INSERT 0x0400
|
||||
#define BCSR_INT_PC1EJECT 0x0800
|
||||
#define BCSR_INT_SD0INSERT 0x1000
|
||||
#define BCSR_INT_SD0EJECT 0x2000
|
||||
#define BCSR_INT_SD1INSERT 0x4000
|
||||
#define BCSR_INT_SD1EJECT 0x8000
|
||||
|
||||
#define DB1200_IDE_PHYS_ADDR 0x18800000
|
||||
#define DB1200_IDE_REG_SHIFT 5
|
||||
#define DB1200_IDE_PHYS_LEN (16 << DB1200_IDE_REG_SHIFT)
|
||||
#define DB1200_ETH_PHYS_ADDR 0x19000300
|
||||
#define DB1200_NAND_PHYS_ADDR 0x20000000
|
||||
|
||||
#define PB1200_IDE_PHYS_ADDR 0x0C800000
|
||||
#define PB1200_ETH_PHYS_ADDR 0x0D000300
|
||||
#define PB1200_NAND_PHYS_ADDR 0x1C000000
|
||||
|
||||
#define DB1200_INT_BEGIN (AU1000_MAX_INTR + 1)
|
||||
#define DB1200_IDE_INT (DB1200_INT_BEGIN + 0)
|
||||
#define DB1200_ETH_INT (DB1200_INT_BEGIN + 1)
|
||||
#define DB1200_PC0_INT (DB1200_INT_BEGIN + 2)
|
||||
#define DB1200_PC0_STSCHG_INT (DB1200_INT_BEGIN + 3)
|
||||
#define DB1200_PC1_INT (DB1200_INT_BEGIN + 4)
|
||||
#define DB1200_PC1_STSCHG_INT (DB1200_INT_BEGIN + 5)
|
||||
#define DB1200_DC_INT (DB1200_INT_BEGIN + 6)
|
||||
#define DB1200_FLASHBUSY_INT (DB1200_INT_BEGIN + 7)
|
||||
#define DB1200_PC0_INSERT_INT (DB1200_INT_BEGIN + 8)
|
||||
#define DB1200_PC0_EJECT_INT (DB1200_INT_BEGIN + 9)
|
||||
#define DB1200_PC1_INSERT_INT (DB1200_INT_BEGIN + 10)
|
||||
#define DB1200_PC1_EJECT_INT (DB1200_INT_BEGIN + 11)
|
||||
#define DB1200_SD0_INSERT_INT (DB1200_INT_BEGIN + 12)
|
||||
#define DB1200_SD0_EJECT_INT (DB1200_INT_BEGIN + 13)
|
||||
#define PB1200_SD1_INSERT_INT (DB1200_INT_BEGIN + 14)
|
||||
#define PB1200_SD1_EJECT_INT (DB1200_INT_BEGIN + 15)
|
||||
#define DB1200_INT_END (DB1200_INT_BEGIN + 15)
|
||||
|
||||
const char *get_system_type(void);
|
||||
|
||||
static int __init db1200_detect_board(void)
|
||||
{
|
||||
int bid;
|
||||
|
||||
/* try the DB1200 first */
|
||||
bcsr_init(DB1200_BCSR_PHYS_ADDR,
|
||||
DB1200_BCSR_PHYS_ADDR + DB1200_BCSR_HEXLED_OFS);
|
||||
if (BCSR_WHOAMI_DB1200 == BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI))) {
|
||||
unsigned short t = bcsr_read(BCSR_HEXLEDS);
|
||||
bcsr_write(BCSR_HEXLEDS, ~t);
|
||||
if (bcsr_read(BCSR_HEXLEDS) != t) {
|
||||
bcsr_write(BCSR_HEXLEDS, t);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* okay, try the PB1200 then */
|
||||
bcsr_init(PB1200_BCSR_PHYS_ADDR,
|
||||
PB1200_BCSR_PHYS_ADDR + PB1200_BCSR_HEXLED_OFS);
|
||||
bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI));
|
||||
if ((bid == BCSR_WHOAMI_PB1200_DDR1) ||
|
||||
(bid == BCSR_WHOAMI_PB1200_DDR2)) {
|
||||
unsigned short t = bcsr_read(BCSR_HEXLEDS);
|
||||
bcsr_write(BCSR_HEXLEDS, ~t);
|
||||
if (bcsr_read(BCSR_HEXLEDS) != t) {
|
||||
bcsr_write(BCSR_HEXLEDS, t);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1; /* it's neither */
|
||||
}
|
||||
|
||||
int __init db1200_board_setup(void)
|
||||
{
|
||||
unsigned short whoami;
|
||||
|
||||
if (db1200_detect_board())
|
||||
return -ENODEV;
|
||||
|
||||
whoami = bcsr_read(BCSR_WHOAMI);
|
||||
switch (BCSR_WHOAMI_BOARD(whoami)) {
|
||||
case BCSR_WHOAMI_PB1200_DDR1:
|
||||
case BCSR_WHOAMI_PB1200_DDR2:
|
||||
case BCSR_WHOAMI_DB1200:
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "Alchemy/AMD/RMI %s Board, CPLD Rev %d"
|
||||
" Board-ID %d Daughtercard ID %d\n", get_system_type(),
|
||||
(whoami >> 4) & 0xf, (whoami >> 8) & 0xf, whoami & 0xf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static struct mtd_partition db1200_spiflash_parts[] = {
|
||||
{
|
||||
.name = "spi_flash",
|
||||
.offset = 0,
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
},
|
||||
};
|
||||
|
||||
static struct flash_platform_data db1200_spiflash_data = {
|
||||
.name = "s25fl001",
|
||||
.parts = db1200_spiflash_parts,
|
||||
.nr_parts = ARRAY_SIZE(db1200_spiflash_parts),
|
||||
.type = "m25p10",
|
||||
};
|
||||
|
||||
static struct spi_board_info db1200_spi_devs[] __initdata = {
|
||||
{
|
||||
/* TI TMP121AIDBVR temp sensor */
|
||||
.modalias = "tmp121",
|
||||
.max_speed_hz = 2000000,
|
||||
.bus_num = 0,
|
||||
.chip_select = 0,
|
||||
.mode = 0,
|
||||
},
|
||||
{
|
||||
/* Spansion S25FL001D0FMA SPI flash */
|
||||
.modalias = "m25p80",
|
||||
.max_speed_hz = 50000000,
|
||||
.bus_num = 0,
|
||||
.chip_select = 1,
|
||||
.mode = 0,
|
||||
.platform_data = &db1200_spiflash_data,
|
||||
},
|
||||
};
|
||||
|
||||
static struct i2c_board_info db1200_i2c_devs[] __initdata = {
|
||||
{ I2C_BOARD_INFO("24c04", 0x52), }, /* AT24C04-10 I2C eeprom */
|
||||
{ I2C_BOARD_INFO("ne1619", 0x2d), }, /* adm1025-compat hwmon */
|
||||
{ I2C_BOARD_INFO("wm8731", 0x1b), }, /* I2S audio codec WM8731 */
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static void au1200_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
unsigned long ioaddr = (unsigned long)this->IO_ADDR_W;
|
||||
|
||||
ioaddr &= 0xffffff00;
|
||||
|
||||
if (ctrl & NAND_CLE) {
|
||||
ioaddr += MEM_STNAND_CMD;
|
||||
} else if (ctrl & NAND_ALE) {
|
||||
ioaddr += MEM_STNAND_ADDR;
|
||||
} else {
|
||||
/* assume we want to r/w real data by default */
|
||||
ioaddr += MEM_STNAND_DATA;
|
||||
}
|
||||
this->IO_ADDR_R = this->IO_ADDR_W = (void __iomem *)ioaddr;
|
||||
if (cmd != NAND_CMD_NONE) {
|
||||
__raw_writeb(cmd, this->IO_ADDR_W);
|
||||
wmb();
|
||||
}
|
||||
}
|
||||
|
||||
static int au1200_nand_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return alchemy_rdsmem(AU1000_MEM_STSTAT) & 1;
|
||||
}
|
||||
|
||||
static struct mtd_partition db1200_nand_parts[] = {
|
||||
{
|
||||
.name = "NAND FS 0",
|
||||
.offset = 0,
|
||||
.size = 8 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
.name = "NAND FS 1",
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.size = MTDPART_SIZ_FULL
|
||||
},
|
||||
};
|
||||
|
||||
struct platform_nand_data db1200_nand_platdata = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.chip_offset = 0,
|
||||
.nr_partitions = ARRAY_SIZE(db1200_nand_parts),
|
||||
.partitions = db1200_nand_parts,
|
||||
.chip_delay = 20,
|
||||
},
|
||||
.ctrl = {
|
||||
.dev_ready = au1200_nand_device_ready,
|
||||
.cmd_ctrl = au1200_nand_cmd_ctrl,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource db1200_nand_res[] = {
|
||||
[0] = {
|
||||
.start = DB1200_NAND_PHYS_ADDR,
|
||||
.end = DB1200_NAND_PHYS_ADDR + 0xff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1200_nand_dev = {
|
||||
.name = "gen_nand",
|
||||
.num_resources = ARRAY_SIZE(db1200_nand_res),
|
||||
.resource = db1200_nand_res,
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &db1200_nand_platdata,
|
||||
}
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct smc91x_platdata db1200_eth_data = {
|
||||
.flags = SMC91X_NOWAIT | SMC91X_USE_16BIT,
|
||||
.leda = RPC_LED_100_10,
|
||||
.ledb = RPC_LED_TX_RX,
|
||||
};
|
||||
|
||||
static struct resource db1200_eth_res[] = {
|
||||
[0] = {
|
||||
.start = DB1200_ETH_PHYS_ADDR,
|
||||
.end = DB1200_ETH_PHYS_ADDR + 0xf,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = DB1200_ETH_INT,
|
||||
.end = DB1200_ETH_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1200_eth_dev = {
|
||||
.dev = {
|
||||
.platform_data = &db1200_eth_data,
|
||||
},
|
||||
.name = "smc91x",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(db1200_eth_res),
|
||||
.resource = db1200_eth_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct pata_platform_info db1200_ide_info = {
|
||||
.ioport_shift = DB1200_IDE_REG_SHIFT,
|
||||
};
|
||||
|
||||
#define IDE_ALT_START (14 << DB1200_IDE_REG_SHIFT)
|
||||
static struct resource db1200_ide_res[] = {
|
||||
[0] = {
|
||||
.start = DB1200_IDE_PHYS_ADDR,
|
||||
.end = DB1200_IDE_PHYS_ADDR + IDE_ALT_START - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = DB1200_IDE_PHYS_ADDR + IDE_ALT_START,
|
||||
.end = DB1200_IDE_PHYS_ADDR + DB1200_IDE_PHYS_LEN - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.start = DB1200_IDE_INT,
|
||||
.end = DB1200_IDE_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static u64 au1200_ide_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device db1200_ide_dev = {
|
||||
.name = "pata_platform",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &au1200_ide_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
.platform_data = &db1200_ide_info,
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(db1200_ide_res),
|
||||
.resource = db1200_ide_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
/* SD carddetects: they're supposed to be edge-triggered, but ack
|
||||
* doesn't seem to work (CPLD Rev 2). Instead, the screaming one
|
||||
* is disabled and its counterpart enabled. The 500ms timeout is
|
||||
* because the carddetect isn't debounced in hardware.
|
||||
*/
|
||||
static irqreturn_t db1200_mmc_cd(int irq, void *ptr)
|
||||
{
|
||||
void(*mmc_cd)(struct mmc_host *, unsigned long);
|
||||
|
||||
if (irq == DB1200_SD0_INSERT_INT) {
|
||||
disable_irq_nosync(DB1200_SD0_INSERT_INT);
|
||||
enable_irq(DB1200_SD0_EJECT_INT);
|
||||
} else {
|
||||
disable_irq_nosync(DB1200_SD0_EJECT_INT);
|
||||
enable_irq(DB1200_SD0_INSERT_INT);
|
||||
}
|
||||
|
||||
/* link against CONFIG_MMC=m */
|
||||
mmc_cd = symbol_get(mmc_detect_change);
|
||||
if (mmc_cd) {
|
||||
mmc_cd(ptr, msecs_to_jiffies(500));
|
||||
symbol_put(mmc_detect_change);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int db1200_mmc_cd_setup(void *mmc_host, int en)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (en) {
|
||||
ret = request_irq(DB1200_SD0_INSERT_INT, db1200_mmc_cd,
|
||||
0, "sd_insert", mmc_host);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = request_irq(DB1200_SD0_EJECT_INT, db1200_mmc_cd,
|
||||
0, "sd_eject", mmc_host);
|
||||
if (ret) {
|
||||
free_irq(DB1200_SD0_INSERT_INT, mmc_host);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bcsr_read(BCSR_SIGSTAT) & BCSR_INT_SD0INSERT)
|
||||
enable_irq(DB1200_SD0_EJECT_INT);
|
||||
else
|
||||
enable_irq(DB1200_SD0_INSERT_INT);
|
||||
|
||||
} else {
|
||||
free_irq(DB1200_SD0_INSERT_INT, mmc_host);
|
||||
free_irq(DB1200_SD0_EJECT_INT, mmc_host);
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void db1200_mmc_set_power(void *mmc_host, int state)
|
||||
{
|
||||
if (state) {
|
||||
bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_SD0PWR);
|
||||
msleep(400); /* stabilization time */
|
||||
} else
|
||||
bcsr_mod(BCSR_BOARD, BCSR_BOARD_SD0PWR, 0);
|
||||
}
|
||||
|
||||
static int db1200_mmc_card_readonly(void *mmc_host)
|
||||
{
|
||||
return (bcsr_read(BCSR_STATUS) & BCSR_STATUS_SD0WP) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int db1200_mmc_card_inserted(void *mmc_host)
|
||||
{
|
||||
return (bcsr_read(BCSR_SIGSTAT) & BCSR_INT_SD0INSERT) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void db1200_mmcled_set(struct led_classdev *led,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
if (brightness != LED_OFF)
|
||||
bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED0, 0);
|
||||
else
|
||||
bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED0);
|
||||
}
|
||||
|
||||
static struct led_classdev db1200_mmc_led = {
|
||||
.brightness_set = db1200_mmcled_set,
|
||||
};
|
||||
|
||||
/* -- */
|
||||
|
||||
static irqreturn_t pb1200_mmc1_cd(int irq, void *ptr)
|
||||
{
|
||||
void(*mmc_cd)(struct mmc_host *, unsigned long);
|
||||
|
||||
if (irq == PB1200_SD1_INSERT_INT) {
|
||||
disable_irq_nosync(PB1200_SD1_INSERT_INT);
|
||||
enable_irq(PB1200_SD1_EJECT_INT);
|
||||
} else {
|
||||
disable_irq_nosync(PB1200_SD1_EJECT_INT);
|
||||
enable_irq(PB1200_SD1_INSERT_INT);
|
||||
}
|
||||
|
||||
/* link against CONFIG_MMC=m */
|
||||
mmc_cd = symbol_get(mmc_detect_change);
|
||||
if (mmc_cd) {
|
||||
mmc_cd(ptr, msecs_to_jiffies(500));
|
||||
symbol_put(mmc_detect_change);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pb1200_mmc1_cd_setup(void *mmc_host, int en)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (en) {
|
||||
ret = request_irq(PB1200_SD1_INSERT_INT, pb1200_mmc1_cd, 0,
|
||||
"sd1_insert", mmc_host);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = request_irq(PB1200_SD1_EJECT_INT, pb1200_mmc1_cd, 0,
|
||||
"sd1_eject", mmc_host);
|
||||
if (ret) {
|
||||
free_irq(PB1200_SD1_INSERT_INT, mmc_host);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bcsr_read(BCSR_SIGSTAT) & BCSR_INT_SD1INSERT)
|
||||
enable_irq(PB1200_SD1_EJECT_INT);
|
||||
else
|
||||
enable_irq(PB1200_SD1_INSERT_INT);
|
||||
|
||||
} else {
|
||||
free_irq(PB1200_SD1_INSERT_INT, mmc_host);
|
||||
free_irq(PB1200_SD1_EJECT_INT, mmc_host);
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pb1200_mmc1led_set(struct led_classdev *led,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
if (brightness != LED_OFF)
|
||||
bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED1, 0);
|
||||
else
|
||||
bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED1);
|
||||
}
|
||||
|
||||
static struct led_classdev pb1200_mmc1_led = {
|
||||
.brightness_set = pb1200_mmc1led_set,
|
||||
};
|
||||
|
||||
static void pb1200_mmc1_set_power(void *mmc_host, int state)
|
||||
{
|
||||
if (state) {
|
||||
bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_SD1PWR);
|
||||
msleep(400); /* stabilization time */
|
||||
} else
|
||||
bcsr_mod(BCSR_BOARD, BCSR_BOARD_SD1PWR, 0);
|
||||
}
|
||||
|
||||
static int pb1200_mmc1_card_readonly(void *mmc_host)
|
||||
{
|
||||
return (bcsr_read(BCSR_STATUS) & BCSR_STATUS_SD1WP) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int pb1200_mmc1_card_inserted(void *mmc_host)
|
||||
{
|
||||
return (bcsr_read(BCSR_SIGSTAT) & BCSR_INT_SD1INSERT) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static struct au1xmmc_platform_data db1200_mmc_platdata[2] = {
|
||||
[0] = {
|
||||
.cd_setup = db1200_mmc_cd_setup,
|
||||
.set_power = db1200_mmc_set_power,
|
||||
.card_inserted = db1200_mmc_card_inserted,
|
||||
.card_readonly = db1200_mmc_card_readonly,
|
||||
.led = &db1200_mmc_led,
|
||||
},
|
||||
[1] = {
|
||||
.cd_setup = pb1200_mmc1_cd_setup,
|
||||
.set_power = pb1200_mmc1_set_power,
|
||||
.card_inserted = pb1200_mmc1_card_inserted,
|
||||
.card_readonly = pb1200_mmc1_card_readonly,
|
||||
.led = &pb1200_mmc1_led,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource au1200_mmc0_resources[] = {
|
||||
[0] = {
|
||||
.start = AU1100_SD0_PHYS_ADDR,
|
||||
.end = AU1100_SD0_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1200_SD_INT,
|
||||
.end = AU1200_SD_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1200_DSCR_CMD0_SDMS_TX0,
|
||||
.end = AU1200_DSCR_CMD0_SDMS_TX0,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1200_DSCR_CMD0_SDMS_RX0,
|
||||
.end = AU1200_DSCR_CMD0_SDMS_RX0,
|
||||
.flags = IORESOURCE_DMA,
|
||||
}
|
||||
};
|
||||
|
||||
static u64 au1xxx_mmc_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device db1200_mmc0_dev = {
|
||||
.name = "au1xxx-mmc",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &au1xxx_mmc_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
.platform_data = &db1200_mmc_platdata[0],
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(au1200_mmc0_resources),
|
||||
.resource = au1200_mmc0_resources,
|
||||
};
|
||||
|
||||
static struct resource au1200_mmc1_res[] = {
|
||||
[0] = {
|
||||
.start = AU1100_SD1_PHYS_ADDR,
|
||||
.end = AU1100_SD1_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1200_SD_INT,
|
||||
.end = AU1200_SD_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1200_DSCR_CMD0_SDMS_TX1,
|
||||
.end = AU1200_DSCR_CMD0_SDMS_TX1,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1200_DSCR_CMD0_SDMS_RX1,
|
||||
.end = AU1200_DSCR_CMD0_SDMS_RX1,
|
||||
.flags = IORESOURCE_DMA,
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device pb1200_mmc1_dev = {
|
||||
.name = "au1xxx-mmc",
|
||||
.id = 1,
|
||||
.dev = {
|
||||
.dma_mask = &au1xxx_mmc_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
.platform_data = &db1200_mmc_platdata[1],
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(au1200_mmc1_res),
|
||||
.resource = au1200_mmc1_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static int db1200fb_panel_index(void)
|
||||
{
|
||||
return (bcsr_read(BCSR_SWITCHES) >> 8) & 0x0f;
|
||||
}
|
||||
|
||||
static int db1200fb_panel_init(void)
|
||||
{
|
||||
/* Apply power */
|
||||
bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD |
|
||||
BCSR_BOARD_LCDBL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int db1200fb_panel_shutdown(void)
|
||||
{
|
||||
/* Remove power */
|
||||
bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD |
|
||||
BCSR_BOARD_LCDBL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct au1200fb_platdata db1200fb_pd = {
|
||||
.panel_index = db1200fb_panel_index,
|
||||
.panel_init = db1200fb_panel_init,
|
||||
.panel_shutdown = db1200fb_panel_shutdown,
|
||||
};
|
||||
|
||||
static struct resource au1200_lcd_res[] = {
|
||||
[0] = {
|
||||
.start = AU1200_LCD_PHYS_ADDR,
|
||||
.end = AU1200_LCD_PHYS_ADDR + 0x800 - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1200_LCD_INT,
|
||||
.end = AU1200_LCD_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static u64 au1200_lcd_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device au1200_lcd_dev = {
|
||||
.name = "au1200-lcd",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &au1200_lcd_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
.platform_data = &db1200fb_pd,
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(au1200_lcd_res),
|
||||
.resource = au1200_lcd_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct resource au1200_psc0_res[] = {
|
||||
[0] = {
|
||||
.start = AU1550_PSC0_PHYS_ADDR,
|
||||
.end = AU1550_PSC0_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1200_PSC0_INT,
|
||||
.end = AU1200_PSC0_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1200_DSCR_CMD0_PSC0_TX,
|
||||
.end = AU1200_DSCR_CMD0_PSC0_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1200_DSCR_CMD0_PSC0_RX,
|
||||
.end = AU1200_DSCR_CMD0_PSC0_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1200_i2c_dev = {
|
||||
.name = "au1xpsc_smbus",
|
||||
.id = 0, /* bus number */
|
||||
.num_resources = ARRAY_SIZE(au1200_psc0_res),
|
||||
.resource = au1200_psc0_res,
|
||||
};
|
||||
|
||||
static void db1200_spi_cs_en(struct au1550_spi_info *spi, int cs, int pol)
|
||||
{
|
||||
if (cs)
|
||||
bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_SPISEL);
|
||||
else
|
||||
bcsr_mod(BCSR_RESETS, BCSR_RESETS_SPISEL, 0);
|
||||
}
|
||||
|
||||
static struct au1550_spi_info db1200_spi_platdata = {
|
||||
.mainclk_hz = 50000000, /* PSC0 clock */
|
||||
.num_chipselect = 2,
|
||||
.activate_cs = db1200_spi_cs_en,
|
||||
};
|
||||
|
||||
static u64 spi_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device db1200_spi_dev = {
|
||||
.dev = {
|
||||
.dma_mask = &spi_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
.platform_data = &db1200_spi_platdata,
|
||||
},
|
||||
.name = "au1550-spi",
|
||||
.id = 0, /* bus number */
|
||||
.num_resources = ARRAY_SIZE(au1200_psc0_res),
|
||||
.resource = au1200_psc0_res,
|
||||
};
|
||||
|
||||
static struct resource au1200_psc1_res[] = {
|
||||
[0] = {
|
||||
.start = AU1550_PSC1_PHYS_ADDR,
|
||||
.end = AU1550_PSC1_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1200_PSC1_INT,
|
||||
.end = AU1200_PSC1_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1200_DSCR_CMD0_PSC1_TX,
|
||||
.end = AU1200_DSCR_CMD0_PSC1_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1200_DSCR_CMD0_PSC1_RX,
|
||||
.end = AU1200_DSCR_CMD0_PSC1_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
/* AC97 or I2S device */
|
||||
static struct platform_device db1200_audio_dev = {
|
||||
/* name assigned later based on switch setting */
|
||||
.id = 1, /* PSC ID */
|
||||
.num_resources = ARRAY_SIZE(au1200_psc1_res),
|
||||
.resource = au1200_psc1_res,
|
||||
};
|
||||
|
||||
/* DB1200 ASoC card device */
|
||||
static struct platform_device db1200_sound_dev = {
|
||||
/* name assigned later based on switch setting */
|
||||
.id = 1, /* PSC ID */
|
||||
};
|
||||
|
||||
static struct platform_device db1200_stac_dev = {
|
||||
.name = "ac97-codec",
|
||||
.id = 1, /* on PSC1 */
|
||||
};
|
||||
|
||||
static struct platform_device db1200_audiodma_dev = {
|
||||
.name = "au1xpsc-pcm",
|
||||
.id = 1, /* PSC ID */
|
||||
};
|
||||
|
||||
static struct platform_device *db1200_devs[] __initdata = {
|
||||
NULL, /* PSC0, selected by S6.8 */
|
||||
&db1200_ide_dev,
|
||||
&db1200_mmc0_dev,
|
||||
&au1200_lcd_dev,
|
||||
&db1200_eth_dev,
|
||||
&db1200_nand_dev,
|
||||
&db1200_audiodma_dev,
|
||||
&db1200_audio_dev,
|
||||
&db1200_stac_dev,
|
||||
&db1200_sound_dev,
|
||||
};
|
||||
|
||||
static struct platform_device *pb1200_devs[] __initdata = {
|
||||
&pb1200_mmc1_dev,
|
||||
};
|
||||
|
||||
/* Some peripheral base addresses differ on the PB1200 */
|
||||
static int __init pb1200_res_fixup(void)
|
||||
{
|
||||
/* CPLD Revs earlier than 4 cause problems */
|
||||
if (BCSR_WHOAMI_CPLD(bcsr_read(BCSR_WHOAMI)) <= 3) {
|
||||
printk(KERN_ERR "WARNING!!!\n");
|
||||
printk(KERN_ERR "WARNING!!!\n");
|
||||
printk(KERN_ERR "PB1200 must be at CPLD rev 4. Please have\n");
|
||||
printk(KERN_ERR "the board updated to latest revisions.\n");
|
||||
printk(KERN_ERR "This software will not work reliably\n");
|
||||
printk(KERN_ERR "on anything older than CPLD rev 4.!\n");
|
||||
printk(KERN_ERR "WARNING!!!\n");
|
||||
printk(KERN_ERR "WARNING!!!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
db1200_nand_res[0].start = PB1200_NAND_PHYS_ADDR;
|
||||
db1200_nand_res[0].end = PB1200_NAND_PHYS_ADDR + 0xff;
|
||||
db1200_ide_res[0].start = PB1200_IDE_PHYS_ADDR;
|
||||
db1200_ide_res[0].end = PB1200_IDE_PHYS_ADDR + DB1200_IDE_PHYS_LEN - 1;
|
||||
db1200_eth_res[0].start = PB1200_ETH_PHYS_ADDR;
|
||||
db1200_eth_res[0].end = PB1200_ETH_PHYS_ADDR + 0xff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init db1200_dev_setup(void)
|
||||
{
|
||||
unsigned long pfc;
|
||||
unsigned short sw;
|
||||
int swapped, bid;
|
||||
struct clk *c;
|
||||
|
||||
bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI));
|
||||
if ((bid == BCSR_WHOAMI_PB1200_DDR1) ||
|
||||
(bid == BCSR_WHOAMI_PB1200_DDR2)) {
|
||||
if (pb1200_res_fixup())
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* GPIO7 is low-level triggered CPLD cascade */
|
||||
irq_set_irq_type(AU1200_GPIO7_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
bcsr_init_irq(DB1200_INT_BEGIN, DB1200_INT_END, AU1200_GPIO7_INT);
|
||||
|
||||
/* SMBus/SPI on PSC0, Audio on PSC1 */
|
||||
pfc = alchemy_rdsys(AU1000_SYS_PINFUNC);
|
||||
pfc &= ~(SYS_PINFUNC_P0A | SYS_PINFUNC_P0B);
|
||||
pfc &= ~(SYS_PINFUNC_P1A | SYS_PINFUNC_P1B | SYS_PINFUNC_FS3);
|
||||
pfc |= SYS_PINFUNC_P1C; /* SPI is configured later */
|
||||
alchemy_wrsys(pfc, AU1000_SYS_PINFUNC);
|
||||
|
||||
/* get 50MHz for I2C driver on PSC0 */
|
||||
c = clk_get(NULL, "psc0_intclk");
|
||||
if (!IS_ERR(c)) {
|
||||
pfc = clk_round_rate(c, 50000000);
|
||||
if ((pfc < 1) || (abs(50000000 - pfc) > 2500000))
|
||||
pr_warn("DB1200: cant get I2C close to 50MHz\n");
|
||||
else
|
||||
clk_set_rate(c, pfc);
|
||||
clk_prepare_enable(c);
|
||||
clk_put(c);
|
||||
}
|
||||
|
||||
/* insert/eject pairs: one of both is always screaming. To avoid
|
||||
* issues they must not be automatically enabled when initially
|
||||
* requested.
|
||||
*/
|
||||
irq_set_status_flags(DB1200_SD0_INSERT_INT, IRQ_NOAUTOEN);
|
||||
irq_set_status_flags(DB1200_SD0_EJECT_INT, IRQ_NOAUTOEN);
|
||||
irq_set_status_flags(DB1200_PC0_INSERT_INT, IRQ_NOAUTOEN);
|
||||
irq_set_status_flags(DB1200_PC0_EJECT_INT, IRQ_NOAUTOEN);
|
||||
irq_set_status_flags(DB1200_PC1_INSERT_INT, IRQ_NOAUTOEN);
|
||||
irq_set_status_flags(DB1200_PC1_EJECT_INT, IRQ_NOAUTOEN);
|
||||
|
||||
i2c_register_board_info(0, db1200_i2c_devs,
|
||||
ARRAY_SIZE(db1200_i2c_devs));
|
||||
spi_register_board_info(db1200_spi_devs,
|
||||
ARRAY_SIZE(db1200_i2c_devs));
|
||||
|
||||
/* SWITCHES: S6.8 I2C/SPI selector (OFF=I2C ON=SPI)
|
||||
* S6.7 AC97/I2S selector (OFF=AC97 ON=I2S)
|
||||
* or S12 on the PB1200.
|
||||
*/
|
||||
|
||||
/* NOTE: GPIO215 controls OTG VBUS supply. In SPI mode however
|
||||
* this pin is claimed by PSC0 (unused though, but pinmux doesn't
|
||||
* allow to free it without crippling the SPI interface).
|
||||
* As a result, in SPI mode, OTG simply won't work (PSC0 uses
|
||||
* it as an input pin which is pulled high on the boards).
|
||||
*/
|
||||
pfc = alchemy_rdsys(AU1000_SYS_PINFUNC) & ~SYS_PINFUNC_P0A;
|
||||
|
||||
/* switch off OTG VBUS supply */
|
||||
gpio_request(215, "otg-vbus");
|
||||
gpio_direction_output(215, 1);
|
||||
|
||||
printk(KERN_INFO "%s device configuration:\n", get_system_type());
|
||||
|
||||
sw = bcsr_read(BCSR_SWITCHES);
|
||||
if (sw & BCSR_SWITCHES_DIP_8) {
|
||||
db1200_devs[0] = &db1200_i2c_dev;
|
||||
bcsr_mod(BCSR_RESETS, BCSR_RESETS_PSC0MUX, 0);
|
||||
|
||||
pfc |= (2 << 17); /* GPIO2 block owns GPIO215 */
|
||||
|
||||
printk(KERN_INFO " S6.8 OFF: PSC0 mode I2C\n");
|
||||
printk(KERN_INFO " OTG port VBUS supply available!\n");
|
||||
} else {
|
||||
db1200_devs[0] = &db1200_spi_dev;
|
||||
bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_PSC0MUX);
|
||||
|
||||
pfc |= (1 << 17); /* PSC0 owns GPIO215 */
|
||||
|
||||
printk(KERN_INFO " S6.8 ON : PSC0 mode SPI\n");
|
||||
printk(KERN_INFO " OTG port VBUS supply disabled\n");
|
||||
}
|
||||
alchemy_wrsys(pfc, AU1000_SYS_PINFUNC);
|
||||
|
||||
/* Audio: DIP7 selects I2S(0)/AC97(1), but need I2C for I2S!
|
||||
* so: DIP7=1 || DIP8=0 => AC97, DIP7=0 && DIP8=1 => I2S
|
||||
*/
|
||||
sw &= BCSR_SWITCHES_DIP_8 | BCSR_SWITCHES_DIP_7;
|
||||
if (sw == BCSR_SWITCHES_DIP_8) {
|
||||
bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_PSC1MUX);
|
||||
db1200_audio_dev.name = "au1xpsc_i2s";
|
||||
db1200_sound_dev.name = "db1200-i2s";
|
||||
printk(KERN_INFO " S6.7 ON : PSC1 mode I2S\n");
|
||||
} else {
|
||||
bcsr_mod(BCSR_RESETS, BCSR_RESETS_PSC1MUX, 0);
|
||||
db1200_audio_dev.name = "au1xpsc_ac97";
|
||||
db1200_sound_dev.name = "db1200-ac97";
|
||||
printk(KERN_INFO " S6.7 OFF: PSC1 mode AC97\n");
|
||||
}
|
||||
|
||||
/* Audio PSC clock is supplied externally. (FIXME: platdata!!) */
|
||||
__raw_writel(PSC_SEL_CLK_SERCLK,
|
||||
(void __iomem *)KSEG1ADDR(AU1550_PSC1_PHYS_ADDR) + PSC_SEL_OFFSET);
|
||||
wmb();
|
||||
|
||||
db1x_register_pcmcia_socket(
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR,
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x000400000 - 1,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x000010000 - 1,
|
||||
DB1200_PC0_INT, DB1200_PC0_INSERT_INT,
|
||||
/*DB1200_PC0_STSCHG_INT*/0, DB1200_PC0_EJECT_INT, 0);
|
||||
|
||||
db1x_register_pcmcia_socket(
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004000000,
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004400000 - 1,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004000000,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004400000 - 1,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x004000000,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x004010000 - 1,
|
||||
DB1200_PC1_INT, DB1200_PC1_INSERT_INT,
|
||||
/*DB1200_PC1_STSCHG_INT*/0, DB1200_PC1_EJECT_INT, 1);
|
||||
|
||||
swapped = bcsr_read(BCSR_STATUS) & BCSR_STATUS_DB1200_SWAPBOOT;
|
||||
db1x_register_norflash(64 << 20, 2, swapped);
|
||||
|
||||
platform_add_devices(db1200_devs, ARRAY_SIZE(db1200_devs));
|
||||
|
||||
/* PB1200 is a DB1200 with a 2nd MMC and Camera connector */
|
||||
if ((bid == BCSR_WHOAMI_PB1200_DDR1) ||
|
||||
(bid == BCSR_WHOAMI_PB1200_DDR2))
|
||||
platform_add_devices(pb1200_devs, ARRAY_SIZE(pb1200_devs));
|
||||
|
||||
return 0;
|
||||
}
|
866
arch/mips/alchemy/devboards/db1300.c
Normal file
866
arch/mips/alchemy/devboards/db1300.c
Normal file
|
@ -0,0 +1,866 @@
|
|||
/*
|
||||
* DBAu1300 init and platform device setup.
|
||||
*
|
||||
* (c) 2009 Manuel Lauss <manuel.lauss@googlemail.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h> /* KEY_* codes */
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/ata_platform.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/smsc911x.h>
|
||||
#include <linux/wm97xx.h>
|
||||
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-au1x00/au1100_mmc.h>
|
||||
#include <asm/mach-au1x00/au1200fb.h>
|
||||
#include <asm/mach-au1x00/au1xxx_dbdma.h>
|
||||
#include <asm/mach-au1x00/au1xxx_psc.h>
|
||||
#include <asm/mach-db1x00/bcsr.h>
|
||||
#include <asm/mach-au1x00/prom.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
/* FPGA (external mux) interrupt sources */
|
||||
#define DB1300_FIRST_INT (ALCHEMY_GPIC_INT_LAST + 1)
|
||||
#define DB1300_IDE_INT (DB1300_FIRST_INT + 0)
|
||||
#define DB1300_ETH_INT (DB1300_FIRST_INT + 1)
|
||||
#define DB1300_CF_INT (DB1300_FIRST_INT + 2)
|
||||
#define DB1300_VIDEO_INT (DB1300_FIRST_INT + 4)
|
||||
#define DB1300_HDMI_INT (DB1300_FIRST_INT + 5)
|
||||
#define DB1300_DC_INT (DB1300_FIRST_INT + 6)
|
||||
#define DB1300_FLASH_INT (DB1300_FIRST_INT + 7)
|
||||
#define DB1300_CF_INSERT_INT (DB1300_FIRST_INT + 8)
|
||||
#define DB1300_CF_EJECT_INT (DB1300_FIRST_INT + 9)
|
||||
#define DB1300_AC97_INT (DB1300_FIRST_INT + 10)
|
||||
#define DB1300_AC97_PEN_INT (DB1300_FIRST_INT + 11)
|
||||
#define DB1300_SD1_INSERT_INT (DB1300_FIRST_INT + 12)
|
||||
#define DB1300_SD1_EJECT_INT (DB1300_FIRST_INT + 13)
|
||||
#define DB1300_OTG_VBUS_OC_INT (DB1300_FIRST_INT + 14)
|
||||
#define DB1300_HOST_VBUS_OC_INT (DB1300_FIRST_INT + 15)
|
||||
#define DB1300_LAST_INT (DB1300_FIRST_INT + 15)
|
||||
|
||||
/* SMSC9210 CS */
|
||||
#define DB1300_ETH_PHYS_ADDR 0x19000000
|
||||
#define DB1300_ETH_PHYS_END 0x197fffff
|
||||
|
||||
/* ATA CS */
|
||||
#define DB1300_IDE_PHYS_ADDR 0x18800000
|
||||
#define DB1300_IDE_REG_SHIFT 5
|
||||
#define DB1300_IDE_PHYS_LEN (16 << DB1300_IDE_REG_SHIFT)
|
||||
|
||||
/* NAND CS */
|
||||
#define DB1300_NAND_PHYS_ADDR 0x20000000
|
||||
#define DB1300_NAND_PHYS_END 0x20000fff
|
||||
|
||||
|
||||
static struct i2c_board_info db1300_i2c_devs[] __initdata = {
|
||||
{ I2C_BOARD_INFO("wm8731", 0x1b), }, /* I2S audio codec */
|
||||
{ I2C_BOARD_INFO("ne1619", 0x2d), }, /* adm1025-compat hwmon */
|
||||
};
|
||||
|
||||
/* multifunction pins to assign to GPIO controller */
|
||||
static int db1300_gpio_pins[] __initdata = {
|
||||
AU1300_PIN_LCDPWM0, AU1300_PIN_PSC2SYNC1, AU1300_PIN_WAKE1,
|
||||
AU1300_PIN_WAKE2, AU1300_PIN_WAKE3, AU1300_PIN_FG3AUX,
|
||||
AU1300_PIN_EXTCLK1,
|
||||
-1, /* terminator */
|
||||
};
|
||||
|
||||
/* multifunction pins to assign to device functions */
|
||||
static int db1300_dev_pins[] __initdata = {
|
||||
/* wake-from-str pins 0-3 */
|
||||
AU1300_PIN_WAKE0,
|
||||
/* external clock sources for PSC0 */
|
||||
AU1300_PIN_EXTCLK0,
|
||||
/* 8bit MMC interface on SD0: 6-9 */
|
||||
AU1300_PIN_SD0DAT4, AU1300_PIN_SD0DAT5, AU1300_PIN_SD0DAT6,
|
||||
AU1300_PIN_SD0DAT7,
|
||||
/* UART1 pins: 11-18 */
|
||||
AU1300_PIN_U1RI, AU1300_PIN_U1DCD, AU1300_PIN_U1DSR,
|
||||
AU1300_PIN_U1CTS, AU1300_PIN_U1RTS, AU1300_PIN_U1DTR,
|
||||
AU1300_PIN_U1RX, AU1300_PIN_U1TX,
|
||||
/* UART0 pins: 19-24 */
|
||||
AU1300_PIN_U0RI, AU1300_PIN_U0DCD, AU1300_PIN_U0DSR,
|
||||
AU1300_PIN_U0CTS, AU1300_PIN_U0RTS, AU1300_PIN_U0DTR,
|
||||
/* UART2: 25-26 */
|
||||
AU1300_PIN_U2RX, AU1300_PIN_U2TX,
|
||||
/* UART3: 27-28 */
|
||||
AU1300_PIN_U3RX, AU1300_PIN_U3TX,
|
||||
/* LCD controller PWMs, ext pixclock: 30-31 */
|
||||
AU1300_PIN_LCDPWM1, AU1300_PIN_LCDCLKIN,
|
||||
/* SD1 interface: 32-37 */
|
||||
AU1300_PIN_SD1DAT0, AU1300_PIN_SD1DAT1, AU1300_PIN_SD1DAT2,
|
||||
AU1300_PIN_SD1DAT3, AU1300_PIN_SD1CMD, AU1300_PIN_SD1CLK,
|
||||
/* SD2 interface: 38-43 */
|
||||
AU1300_PIN_SD2DAT0, AU1300_PIN_SD2DAT1, AU1300_PIN_SD2DAT2,
|
||||
AU1300_PIN_SD2DAT3, AU1300_PIN_SD2CMD, AU1300_PIN_SD2CLK,
|
||||
/* PSC0/1 clocks: 44-45 */
|
||||
AU1300_PIN_PSC0CLK, AU1300_PIN_PSC1CLK,
|
||||
/* PSCs: 46-49/50-53/54-57/58-61 */
|
||||
AU1300_PIN_PSC0SYNC0, AU1300_PIN_PSC0SYNC1, AU1300_PIN_PSC0D0,
|
||||
AU1300_PIN_PSC0D1,
|
||||
AU1300_PIN_PSC1SYNC0, AU1300_PIN_PSC1SYNC1, AU1300_PIN_PSC1D0,
|
||||
AU1300_PIN_PSC1D1,
|
||||
AU1300_PIN_PSC2SYNC0, AU1300_PIN_PSC2D0,
|
||||
AU1300_PIN_PSC2D1,
|
||||
AU1300_PIN_PSC3SYNC0, AU1300_PIN_PSC3SYNC1, AU1300_PIN_PSC3D0,
|
||||
AU1300_PIN_PSC3D1,
|
||||
/* PCMCIA interface: 62-70 */
|
||||
AU1300_PIN_PCE2, AU1300_PIN_PCE1, AU1300_PIN_PIOS16,
|
||||
AU1300_PIN_PIOR, AU1300_PIN_PWE, AU1300_PIN_PWAIT,
|
||||
AU1300_PIN_PREG, AU1300_PIN_POE, AU1300_PIN_PIOW,
|
||||
/* camera interface H/V sync inputs: 71-72 */
|
||||
AU1300_PIN_CIMLS, AU1300_PIN_CIMFS,
|
||||
/* PSC2/3 clocks: 73-74 */
|
||||
AU1300_PIN_PSC2CLK, AU1300_PIN_PSC3CLK,
|
||||
-1, /* terminator */
|
||||
};
|
||||
|
||||
static void __init db1300_gpio_config(void)
|
||||
{
|
||||
int *i;
|
||||
|
||||
i = &db1300_dev_pins[0];
|
||||
while (*i != -1)
|
||||
au1300_pinfunc_to_dev(*i++);
|
||||
|
||||
i = &db1300_gpio_pins[0];
|
||||
while (*i != -1)
|
||||
au1300_gpio_direction_input(*i++);/* implies pin_to_gpio */
|
||||
|
||||
au1300_set_dbdma_gpio(1, AU1300_PIN_FG3AUX);
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static void au1300_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
unsigned long ioaddr = (unsigned long)this->IO_ADDR_W;
|
||||
|
||||
ioaddr &= 0xffffff00;
|
||||
|
||||
if (ctrl & NAND_CLE) {
|
||||
ioaddr += MEM_STNAND_CMD;
|
||||
} else if (ctrl & NAND_ALE) {
|
||||
ioaddr += MEM_STNAND_ADDR;
|
||||
} else {
|
||||
/* assume we want to r/w real data by default */
|
||||
ioaddr += MEM_STNAND_DATA;
|
||||
}
|
||||
this->IO_ADDR_R = this->IO_ADDR_W = (void __iomem *)ioaddr;
|
||||
if (cmd != NAND_CMD_NONE) {
|
||||
__raw_writeb(cmd, this->IO_ADDR_W);
|
||||
wmb();
|
||||
}
|
||||
}
|
||||
|
||||
static int au1300_nand_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return alchemy_rdsmem(AU1000_MEM_STSTAT) & 1;
|
||||
}
|
||||
|
||||
static struct mtd_partition db1300_nand_parts[] = {
|
||||
{
|
||||
.name = "NAND FS 0",
|
||||
.offset = 0,
|
||||
.size = 8 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
.name = "NAND FS 1",
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.size = MTDPART_SIZ_FULL
|
||||
},
|
||||
};
|
||||
|
||||
struct platform_nand_data db1300_nand_platdata = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.chip_offset = 0,
|
||||
.nr_partitions = ARRAY_SIZE(db1300_nand_parts),
|
||||
.partitions = db1300_nand_parts,
|
||||
.chip_delay = 20,
|
||||
},
|
||||
.ctrl = {
|
||||
.dev_ready = au1300_nand_device_ready,
|
||||
.cmd_ctrl = au1300_nand_cmd_ctrl,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource db1300_nand_res[] = {
|
||||
[0] = {
|
||||
.start = DB1300_NAND_PHYS_ADDR,
|
||||
.end = DB1300_NAND_PHYS_ADDR + 0xff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1300_nand_dev = {
|
||||
.name = "gen_nand",
|
||||
.num_resources = ARRAY_SIZE(db1300_nand_res),
|
||||
.resource = db1300_nand_res,
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &db1300_nand_platdata,
|
||||
}
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct resource db1300_eth_res[] = {
|
||||
[0] = {
|
||||
.start = DB1300_ETH_PHYS_ADDR,
|
||||
.end = DB1300_ETH_PHYS_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = DB1300_ETH_INT,
|
||||
.end = DB1300_ETH_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct smsc911x_platform_config db1300_eth_config = {
|
||||
.phy_interface = PHY_INTERFACE_MODE_MII,
|
||||
.irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
|
||||
.irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL,
|
||||
.flags = SMSC911X_USE_32BIT,
|
||||
};
|
||||
|
||||
static struct platform_device db1300_eth_dev = {
|
||||
.name = "smsc911x",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(db1300_eth_res),
|
||||
.resource = db1300_eth_res,
|
||||
.dev = {
|
||||
.platform_data = &db1300_eth_config,
|
||||
},
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct resource au1300_psc1_res[] = {
|
||||
[0] = {
|
||||
.start = AU1300_PSC1_PHYS_ADDR,
|
||||
.end = AU1300_PSC1_PHYS_ADDR + 0x0fff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1300_PSC1_INT,
|
||||
.end = AU1300_PSC1_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1300_DSCR_CMD0_PSC1_TX,
|
||||
.end = AU1300_DSCR_CMD0_PSC1_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1300_DSCR_CMD0_PSC1_RX,
|
||||
.end = AU1300_DSCR_CMD0_PSC1_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1300_ac97_dev = {
|
||||
.name = "au1xpsc_ac97",
|
||||
.id = 1, /* PSC ID. match with AC97 codec ID! */
|
||||
.num_resources = ARRAY_SIZE(au1300_psc1_res),
|
||||
.resource = au1300_psc1_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct resource au1300_psc2_res[] = {
|
||||
[0] = {
|
||||
.start = AU1300_PSC2_PHYS_ADDR,
|
||||
.end = AU1300_PSC2_PHYS_ADDR + 0x0fff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1300_PSC2_INT,
|
||||
.end = AU1300_PSC2_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1300_DSCR_CMD0_PSC2_TX,
|
||||
.end = AU1300_DSCR_CMD0_PSC2_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1300_DSCR_CMD0_PSC2_RX,
|
||||
.end = AU1300_DSCR_CMD0_PSC2_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1300_i2s_dev = {
|
||||
.name = "au1xpsc_i2s",
|
||||
.id = 2, /* PSC ID */
|
||||
.num_resources = ARRAY_SIZE(au1300_psc2_res),
|
||||
.resource = au1300_psc2_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct resource au1300_psc3_res[] = {
|
||||
[0] = {
|
||||
.start = AU1300_PSC3_PHYS_ADDR,
|
||||
.end = AU1300_PSC3_PHYS_ADDR + 0x0fff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1300_PSC3_INT,
|
||||
.end = AU1300_PSC3_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1300_DSCR_CMD0_PSC3_TX,
|
||||
.end = AU1300_DSCR_CMD0_PSC3_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1300_DSCR_CMD0_PSC3_RX,
|
||||
.end = AU1300_DSCR_CMD0_PSC3_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1300_i2c_dev = {
|
||||
.name = "au1xpsc_smbus",
|
||||
.id = 0, /* bus number */
|
||||
.num_resources = ARRAY_SIZE(au1300_psc3_res),
|
||||
.resource = au1300_psc3_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
/* proper key assignments when facing the LCD panel. For key assignments
|
||||
* according to the schematics swap up with down and left with right.
|
||||
* I chose to use it to emulate the arrow keys of a keyboard.
|
||||
*/
|
||||
static struct gpio_keys_button db1300_5waysw_arrowkeys[] = {
|
||||
{
|
||||
.code = KEY_DOWN,
|
||||
.gpio = AU1300_PIN_LCDPWM0,
|
||||
.type = EV_KEY,
|
||||
.debounce_interval = 1,
|
||||
.active_low = 1,
|
||||
.desc = "5waysw-down",
|
||||
},
|
||||
{
|
||||
.code = KEY_UP,
|
||||
.gpio = AU1300_PIN_PSC2SYNC1,
|
||||
.type = EV_KEY,
|
||||
.debounce_interval = 1,
|
||||
.active_low = 1,
|
||||
.desc = "5waysw-up",
|
||||
},
|
||||
{
|
||||
.code = KEY_RIGHT,
|
||||
.gpio = AU1300_PIN_WAKE3,
|
||||
.type = EV_KEY,
|
||||
.debounce_interval = 1,
|
||||
.active_low = 1,
|
||||
.desc = "5waysw-right",
|
||||
},
|
||||
{
|
||||
.code = KEY_LEFT,
|
||||
.gpio = AU1300_PIN_WAKE2,
|
||||
.type = EV_KEY,
|
||||
.debounce_interval = 1,
|
||||
.active_low = 1,
|
||||
.desc = "5waysw-left",
|
||||
},
|
||||
{
|
||||
.code = KEY_ENTER,
|
||||
.gpio = AU1300_PIN_WAKE1,
|
||||
.type = EV_KEY,
|
||||
.debounce_interval = 1,
|
||||
.active_low = 1,
|
||||
.desc = "5waysw-push",
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_keys_platform_data db1300_5waysw_data = {
|
||||
.buttons = db1300_5waysw_arrowkeys,
|
||||
.nbuttons = ARRAY_SIZE(db1300_5waysw_arrowkeys),
|
||||
.rep = 1,
|
||||
.name = "db1300-5wayswitch",
|
||||
};
|
||||
|
||||
static struct platform_device db1300_5waysw_dev = {
|
||||
.name = "gpio-keys",
|
||||
.dev = {
|
||||
.platform_data = &db1300_5waysw_data,
|
||||
},
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct pata_platform_info db1300_ide_info = {
|
||||
.ioport_shift = DB1300_IDE_REG_SHIFT,
|
||||
};
|
||||
|
||||
#define IDE_ALT_START (14 << DB1300_IDE_REG_SHIFT)
|
||||
static struct resource db1300_ide_res[] = {
|
||||
[0] = {
|
||||
.start = DB1300_IDE_PHYS_ADDR,
|
||||
.end = DB1300_IDE_PHYS_ADDR + IDE_ALT_START - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = DB1300_IDE_PHYS_ADDR + IDE_ALT_START,
|
||||
.end = DB1300_IDE_PHYS_ADDR + DB1300_IDE_PHYS_LEN - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.start = DB1300_IDE_INT,
|
||||
.end = DB1300_IDE_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1300_ide_dev = {
|
||||
.dev = {
|
||||
.platform_data = &db1300_ide_info,
|
||||
},
|
||||
.name = "pata_platform",
|
||||
.resource = db1300_ide_res,
|
||||
.num_resources = ARRAY_SIZE(db1300_ide_res),
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static irqreturn_t db1300_mmc_cd(int irq, void *ptr)
|
||||
{
|
||||
void(*mmc_cd)(struct mmc_host *, unsigned long);
|
||||
|
||||
/* disable the one currently screaming. No other way to shut it up */
|
||||
if (irq == DB1300_SD1_INSERT_INT) {
|
||||
disable_irq_nosync(DB1300_SD1_INSERT_INT);
|
||||
enable_irq(DB1300_SD1_EJECT_INT);
|
||||
} else {
|
||||
disable_irq_nosync(DB1300_SD1_EJECT_INT);
|
||||
enable_irq(DB1300_SD1_INSERT_INT);
|
||||
}
|
||||
|
||||
/* link against CONFIG_MMC=m. We can only be called once MMC core has
|
||||
* initialized the controller, so symbol_get() should always succeed.
|
||||
*/
|
||||
mmc_cd = symbol_get(mmc_detect_change);
|
||||
mmc_cd(ptr, msecs_to_jiffies(500));
|
||||
symbol_put(mmc_detect_change);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int db1300_mmc_card_readonly(void *mmc_host)
|
||||
{
|
||||
/* it uses SD1 interface, but the DB1200's SD0 bit in the CPLD */
|
||||
return bcsr_read(BCSR_STATUS) & BCSR_STATUS_SD0WP;
|
||||
}
|
||||
|
||||
static int db1300_mmc_card_inserted(void *mmc_host)
|
||||
{
|
||||
return bcsr_read(BCSR_SIGSTAT) & (1 << 12); /* insertion irq signal */
|
||||
}
|
||||
|
||||
static int db1300_mmc_cd_setup(void *mmc_host, int en)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (en) {
|
||||
ret = request_irq(DB1300_SD1_INSERT_INT, db1300_mmc_cd, 0,
|
||||
"sd_insert", mmc_host);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = request_irq(DB1300_SD1_EJECT_INT, db1300_mmc_cd, 0,
|
||||
"sd_eject", mmc_host);
|
||||
if (ret) {
|
||||
free_irq(DB1300_SD1_INSERT_INT, mmc_host);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (db1300_mmc_card_inserted(mmc_host))
|
||||
enable_irq(DB1300_SD1_EJECT_INT);
|
||||
else
|
||||
enable_irq(DB1300_SD1_INSERT_INT);
|
||||
|
||||
} else {
|
||||
free_irq(DB1300_SD1_INSERT_INT, mmc_host);
|
||||
free_irq(DB1300_SD1_EJECT_INT, mmc_host);
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void db1300_mmcled_set(struct led_classdev *led,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
if (brightness != LED_OFF)
|
||||
bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED0, 0);
|
||||
else
|
||||
bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED0);
|
||||
}
|
||||
|
||||
static struct led_classdev db1300_mmc_led = {
|
||||
.brightness_set = db1300_mmcled_set,
|
||||
};
|
||||
|
||||
struct au1xmmc_platform_data db1300_sd1_platdata = {
|
||||
.cd_setup = db1300_mmc_cd_setup,
|
||||
.card_inserted = db1300_mmc_card_inserted,
|
||||
.card_readonly = db1300_mmc_card_readonly,
|
||||
.led = &db1300_mmc_led,
|
||||
};
|
||||
|
||||
static struct resource au1300_sd1_res[] = {
|
||||
[0] = {
|
||||
.start = AU1300_SD1_PHYS_ADDR,
|
||||
.end = AU1300_SD1_PHYS_ADDR,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1300_SD1_INT,
|
||||
.end = AU1300_SD1_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1300_DSCR_CMD0_SDMS_TX1,
|
||||
.end = AU1300_DSCR_CMD0_SDMS_TX1,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1300_DSCR_CMD0_SDMS_RX1,
|
||||
.end = AU1300_DSCR_CMD0_SDMS_RX1,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1300_sd1_dev = {
|
||||
.dev = {
|
||||
.platform_data = &db1300_sd1_platdata,
|
||||
},
|
||||
.name = "au1xxx-mmc",
|
||||
.id = 1,
|
||||
.resource = au1300_sd1_res,
|
||||
.num_resources = ARRAY_SIZE(au1300_sd1_res),
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static int db1300_movinand_inserted(void *mmc_host)
|
||||
{
|
||||
return 0; /* disable for now, it doesn't work yet */
|
||||
}
|
||||
|
||||
static int db1300_movinand_readonly(void *mmc_host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void db1300_movinand_led_set(struct led_classdev *led,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
if (brightness != LED_OFF)
|
||||
bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED1, 0);
|
||||
else
|
||||
bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED1);
|
||||
}
|
||||
|
||||
static struct led_classdev db1300_movinand_led = {
|
||||
.brightness_set = db1300_movinand_led_set,
|
||||
};
|
||||
|
||||
struct au1xmmc_platform_data db1300_sd0_platdata = {
|
||||
.card_inserted = db1300_movinand_inserted,
|
||||
.card_readonly = db1300_movinand_readonly,
|
||||
.led = &db1300_movinand_led,
|
||||
.mask_host_caps = MMC_CAP_NEEDS_POLL,
|
||||
};
|
||||
|
||||
static struct resource au1300_sd0_res[] = {
|
||||
[0] = {
|
||||
.start = AU1100_SD0_PHYS_ADDR,
|
||||
.end = AU1100_SD0_PHYS_ADDR,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1300_SD0_INT,
|
||||
.end = AU1300_SD0_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1300_DSCR_CMD0_SDMS_TX0,
|
||||
.end = AU1300_DSCR_CMD0_SDMS_TX0,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1300_DSCR_CMD0_SDMS_RX0,
|
||||
.end = AU1300_DSCR_CMD0_SDMS_RX0,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1300_sd0_dev = {
|
||||
.dev = {
|
||||
.platform_data = &db1300_sd0_platdata,
|
||||
},
|
||||
.name = "au1xxx-mmc",
|
||||
.id = 0,
|
||||
.resource = au1300_sd0_res,
|
||||
.num_resources = ARRAY_SIZE(au1300_sd0_res),
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct platform_device db1300_wm9715_dev = {
|
||||
.name = "wm9712-codec",
|
||||
.id = 1, /* ID of PSC for AC97 audio, see asoc glue! */
|
||||
};
|
||||
|
||||
static struct platform_device db1300_ac97dma_dev = {
|
||||
.name = "au1xpsc-pcm",
|
||||
.id = 1, /* PSC ID */
|
||||
};
|
||||
|
||||
static struct platform_device db1300_i2sdma_dev = {
|
||||
.name = "au1xpsc-pcm",
|
||||
.id = 2, /* PSC ID */
|
||||
};
|
||||
|
||||
static struct platform_device db1300_sndac97_dev = {
|
||||
.name = "db1300-ac97",
|
||||
};
|
||||
|
||||
static struct platform_device db1300_sndi2s_dev = {
|
||||
.name = "db1300-i2s",
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static int db1300fb_panel_index(void)
|
||||
{
|
||||
return 9; /* DB1300_800x480 */
|
||||
}
|
||||
|
||||
static int db1300fb_panel_init(void)
|
||||
{
|
||||
/* Apply power (Vee/Vdd logic is inverted on Panel DB1300_800x480) */
|
||||
bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD,
|
||||
BCSR_BOARD_LCDBL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int db1300fb_panel_shutdown(void)
|
||||
{
|
||||
/* Remove power (Vee/Vdd logic is inverted on Panel DB1300_800x480) */
|
||||
bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDBL,
|
||||
BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct au1200fb_platdata db1300fb_pd = {
|
||||
.panel_index = db1300fb_panel_index,
|
||||
.panel_init = db1300fb_panel_init,
|
||||
.panel_shutdown = db1300fb_panel_shutdown,
|
||||
};
|
||||
|
||||
static struct resource au1300_lcd_res[] = {
|
||||
[0] = {
|
||||
.start = AU1200_LCD_PHYS_ADDR,
|
||||
.end = AU1200_LCD_PHYS_ADDR + 0x800 - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1300_LCD_INT,
|
||||
.end = AU1300_LCD_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static u64 au1300_lcd_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device db1300_lcd_dev = {
|
||||
.name = "au1200-lcd",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &au1300_lcd_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
.platform_data = &db1300fb_pd,
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(au1300_lcd_res),
|
||||
.resource = au1300_lcd_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static void db1300_wm97xx_irqen(struct wm97xx *wm, int enable)
|
||||
{
|
||||
if (enable)
|
||||
enable_irq(DB1300_AC97_PEN_INT);
|
||||
else
|
||||
disable_irq_nosync(DB1300_AC97_PEN_INT);
|
||||
}
|
||||
|
||||
static struct wm97xx_mach_ops db1300_wm97xx_ops = {
|
||||
.irq_enable = db1300_wm97xx_irqen,
|
||||
.irq_gpio = WM97XX_GPIO_3,
|
||||
};
|
||||
|
||||
static int db1300_wm97xx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wm97xx *wm = platform_get_drvdata(pdev);
|
||||
|
||||
/* external pendown indicator */
|
||||
wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
|
||||
WM97XX_GPIO_POL_LOW, WM97XX_GPIO_STICKY,
|
||||
WM97XX_GPIO_WAKE);
|
||||
|
||||
/* internal "virtual" pendown gpio */
|
||||
wm97xx_config_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT,
|
||||
WM97XX_GPIO_POL_LOW, WM97XX_GPIO_NOTSTICKY,
|
||||
WM97XX_GPIO_NOWAKE);
|
||||
|
||||
wm->pen_irq = DB1300_AC97_PEN_INT;
|
||||
|
||||
return wm97xx_register_mach_ops(wm, &db1300_wm97xx_ops);
|
||||
}
|
||||
|
||||
static struct platform_driver db1300_wm97xx_driver = {
|
||||
.driver.name = "wm97xx-touch",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = db1300_wm97xx_probe,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct platform_device *db1300_dev[] __initdata = {
|
||||
&db1300_eth_dev,
|
||||
&db1300_i2c_dev,
|
||||
&db1300_5waysw_dev,
|
||||
&db1300_nand_dev,
|
||||
&db1300_ide_dev,
|
||||
&db1300_sd0_dev,
|
||||
&db1300_sd1_dev,
|
||||
&db1300_lcd_dev,
|
||||
&db1300_ac97_dev,
|
||||
&db1300_i2s_dev,
|
||||
&db1300_wm9715_dev,
|
||||
&db1300_ac97dma_dev,
|
||||
&db1300_i2sdma_dev,
|
||||
&db1300_sndac97_dev,
|
||||
&db1300_sndi2s_dev,
|
||||
};
|
||||
|
||||
int __init db1300_dev_setup(void)
|
||||
{
|
||||
int swapped, cpldirq;
|
||||
struct clk *c;
|
||||
|
||||
/* setup CPLD IRQ muxer */
|
||||
cpldirq = au1300_gpio_to_irq(AU1300_PIN_EXTCLK1);
|
||||
irq_set_irq_type(cpldirq, IRQ_TYPE_LEVEL_HIGH);
|
||||
bcsr_init_irq(DB1300_FIRST_INT, DB1300_LAST_INT, cpldirq);
|
||||
|
||||
/* insert/eject IRQs: one always triggers so don't enable them
|
||||
* when doing request_irq() on them. DB1200 has this bug too.
|
||||
*/
|
||||
irq_set_status_flags(DB1300_SD1_INSERT_INT, IRQ_NOAUTOEN);
|
||||
irq_set_status_flags(DB1300_SD1_EJECT_INT, IRQ_NOAUTOEN);
|
||||
irq_set_status_flags(DB1300_CF_INSERT_INT, IRQ_NOAUTOEN);
|
||||
irq_set_status_flags(DB1300_CF_EJECT_INT, IRQ_NOAUTOEN);
|
||||
|
||||
/*
|
||||
* setup board
|
||||
*/
|
||||
prom_get_ethernet_addr(&db1300_eth_config.mac[0]);
|
||||
|
||||
i2c_register_board_info(0, db1300_i2c_devs,
|
||||
ARRAY_SIZE(db1300_i2c_devs));
|
||||
|
||||
if (platform_driver_register(&db1300_wm97xx_driver))
|
||||
pr_warn("DB1300: failed to init touch pen irq support!\n");
|
||||
|
||||
/* Audio PSC clock is supplied by codecs (PSC1, 2) */
|
||||
__raw_writel(PSC_SEL_CLK_SERCLK,
|
||||
(void __iomem *)KSEG1ADDR(AU1300_PSC1_PHYS_ADDR) + PSC_SEL_OFFSET);
|
||||
wmb();
|
||||
__raw_writel(PSC_SEL_CLK_SERCLK,
|
||||
(void __iomem *)KSEG1ADDR(AU1300_PSC2_PHYS_ADDR) + PSC_SEL_OFFSET);
|
||||
wmb();
|
||||
/* I2C driver wants 50MHz, get as close as possible */
|
||||
c = clk_get(NULL, "psc3_intclk");
|
||||
if (!IS_ERR(c)) {
|
||||
clk_set_rate(c, 50000000);
|
||||
clk_prepare_enable(c);
|
||||
clk_put(c);
|
||||
}
|
||||
__raw_writel(PSC_SEL_CLK_INTCLK,
|
||||
(void __iomem *)KSEG1ADDR(AU1300_PSC3_PHYS_ADDR) + PSC_SEL_OFFSET);
|
||||
wmb();
|
||||
|
||||
/* enable power to USB ports */
|
||||
bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_USBHPWR | BCSR_RESETS_OTGPWR);
|
||||
|
||||
/* although it is socket #0, it uses the CPLD bits which previous boards
|
||||
* have used for socket #1.
|
||||
*/
|
||||
db1x_register_pcmcia_socket(
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR,
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x00400000 - 1,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x00400000 - 1,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x00010000 - 1,
|
||||
DB1300_CF_INT, DB1300_CF_INSERT_INT, 0, DB1300_CF_EJECT_INT, 1);
|
||||
|
||||
swapped = bcsr_read(BCSR_STATUS) & BCSR_STATUS_DB1200_SWAPBOOT;
|
||||
db1x_register_norflash(64 << 20, 2, swapped);
|
||||
|
||||
return platform_add_devices(db1300_dev, ARRAY_SIZE(db1300_dev));
|
||||
}
|
||||
|
||||
|
||||
int __init db1300_board_setup(void)
|
||||
{
|
||||
unsigned short whoami;
|
||||
|
||||
bcsr_init(DB1300_BCSR_PHYS_ADDR,
|
||||
DB1300_BCSR_PHYS_ADDR + DB1300_BCSR_HEXLED_OFS);
|
||||
|
||||
whoami = bcsr_read(BCSR_WHOAMI);
|
||||
if (BCSR_WHOAMI_BOARD(whoami) != BCSR_WHOAMI_DB1300)
|
||||
return -ENODEV;
|
||||
|
||||
db1300_gpio_config();
|
||||
|
||||
printk(KERN_INFO "NetLogic DBAu1300 Development Platform.\n\t"
|
||||
"BoardID %d CPLD Rev %d DaughtercardID %d\n",
|
||||
BCSR_WHOAMI_BOARD(whoami), BCSR_WHOAMI_CPLD(whoami),
|
||||
BCSR_WHOAMI_DCID(whoami));
|
||||
|
||||
/* enable UARTs, YAMON only enables #2 */
|
||||
alchemy_uart_enable(AU1300_UART0_PHYS_ADDR);
|
||||
alchemy_uart_enable(AU1300_UART1_PHYS_ADDR);
|
||||
alchemy_uart_enable(AU1300_UART3_PHYS_ADDR);
|
||||
|
||||
return 0;
|
||||
}
|
619
arch/mips/alchemy/devboards/db1550.c
Normal file
619
arch/mips/alchemy/devboards/db1550.c
Normal file
|
@ -0,0 +1,619 @@
|
|||
/*
|
||||
* Alchemy Db1550/Pb1550 board support
|
||||
*
|
||||
* (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/flash.h>
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-au1x00/au1xxx_eth.h>
|
||||
#include <asm/mach-au1x00/au1xxx_dbdma.h>
|
||||
#include <asm/mach-au1x00/au1xxx_psc.h>
|
||||
#include <asm/mach-au1x00/au1550_spi.h>
|
||||
#include <asm/mach-au1x00/au1550nd.h>
|
||||
#include <asm/mach-db1x00/bcsr.h>
|
||||
#include <prom.h>
|
||||
#include "platform.h"
|
||||
|
||||
static void __init db1550_hw_setup(void)
|
||||
{
|
||||
void __iomem *base;
|
||||
unsigned long v;
|
||||
|
||||
/* complete pin setup: assign GPIO16 to PSC0_SYNC1 (SPI cs# line)
|
||||
* as well as PSC1_SYNC for AC97 on PB1550.
|
||||
*/
|
||||
v = alchemy_rdsys(AU1000_SYS_PINFUNC);
|
||||
alchemy_wrsys(v | 1 | SYS_PF_PSC1_S1, AU1000_SYS_PINFUNC);
|
||||
|
||||
/* reset the AC97 codec now, the reset time in the psc-ac97 driver
|
||||
* is apparently too short although it's ridiculous as it is.
|
||||
*/
|
||||
base = (void __iomem *)KSEG1ADDR(AU1550_PSC1_PHYS_ADDR);
|
||||
__raw_writel(PSC_SEL_CLK_SERCLK | PSC_SEL_PS_AC97MODE,
|
||||
base + PSC_SEL_OFFSET);
|
||||
__raw_writel(PSC_CTRL_DISABLE, base + PSC_CTRL_OFFSET);
|
||||
wmb();
|
||||
__raw_writel(PSC_AC97RST_RST, base + PSC_AC97RST_OFFSET);
|
||||
wmb();
|
||||
}
|
||||
|
||||
int __init db1550_board_setup(void)
|
||||
{
|
||||
unsigned short whoami;
|
||||
|
||||
bcsr_init(DB1550_BCSR_PHYS_ADDR,
|
||||
DB1550_BCSR_PHYS_ADDR + DB1550_BCSR_HEXLED_OFS);
|
||||
|
||||
whoami = bcsr_read(BCSR_WHOAMI); /* PB1550 hexled offset differs */
|
||||
switch (BCSR_WHOAMI_BOARD(whoami)) {
|
||||
case BCSR_WHOAMI_PB1550_SDR:
|
||||
case BCSR_WHOAMI_PB1550_DDR:
|
||||
bcsr_init(PB1550_BCSR_PHYS_ADDR,
|
||||
PB1550_BCSR_PHYS_ADDR + PB1550_BCSR_HEXLED_OFS);
|
||||
case BCSR_WHOAMI_DB1550:
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pr_info("Alchemy/AMD %s Board, CPLD Rev %d Board-ID %d " \
|
||||
"Daughtercard ID %d\n", get_system_type(),
|
||||
(whoami >> 4) & 0xf, (whoami >> 8) & 0xf, whoami & 0xf);
|
||||
|
||||
db1550_hw_setup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static struct mtd_partition db1550_spiflash_parts[] = {
|
||||
{
|
||||
.name = "spi_flash",
|
||||
.offset = 0,
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
},
|
||||
};
|
||||
|
||||
static struct flash_platform_data db1550_spiflash_data = {
|
||||
.name = "s25fl010",
|
||||
.parts = db1550_spiflash_parts,
|
||||
.nr_parts = ARRAY_SIZE(db1550_spiflash_parts),
|
||||
.type = "m25p10",
|
||||
};
|
||||
|
||||
static struct spi_board_info db1550_spi_devs[] __initdata = {
|
||||
{
|
||||
/* TI TMP121AIDBVR temp sensor */
|
||||
.modalias = "tmp121",
|
||||
.max_speed_hz = 2400000,
|
||||
.bus_num = 0,
|
||||
.chip_select = 0,
|
||||
.mode = SPI_MODE_0,
|
||||
},
|
||||
{
|
||||
/* Spansion S25FL001D0FMA SPI flash */
|
||||
.modalias = "m25p80",
|
||||
.max_speed_hz = 2400000,
|
||||
.bus_num = 0,
|
||||
.chip_select = 1,
|
||||
.mode = SPI_MODE_0,
|
||||
.platform_data = &db1550_spiflash_data,
|
||||
},
|
||||
};
|
||||
|
||||
static struct i2c_board_info db1550_i2c_devs[] __initdata = {
|
||||
{ I2C_BOARD_INFO("24c04", 0x52),}, /* AT24C04-10 I2C eeprom */
|
||||
{ I2C_BOARD_INFO("ne1619", 0x2d),}, /* adm1025-compat hwmon */
|
||||
{ I2C_BOARD_INFO("wm8731", 0x1b),}, /* I2S audio codec WM8731 */
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static void au1550_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
unsigned long ioaddr = (unsigned long)this->IO_ADDR_W;
|
||||
|
||||
ioaddr &= 0xffffff00;
|
||||
|
||||
if (ctrl & NAND_CLE) {
|
||||
ioaddr += MEM_STNAND_CMD;
|
||||
} else if (ctrl & NAND_ALE) {
|
||||
ioaddr += MEM_STNAND_ADDR;
|
||||
} else {
|
||||
/* assume we want to r/w real data by default */
|
||||
ioaddr += MEM_STNAND_DATA;
|
||||
}
|
||||
this->IO_ADDR_R = this->IO_ADDR_W = (void __iomem *)ioaddr;
|
||||
if (cmd != NAND_CMD_NONE) {
|
||||
__raw_writeb(cmd, this->IO_ADDR_W);
|
||||
wmb();
|
||||
}
|
||||
}
|
||||
|
||||
static int au1550_nand_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return alchemy_rdsmem(AU1000_MEM_STSTAT) & 1;
|
||||
}
|
||||
|
||||
static struct mtd_partition db1550_nand_parts[] = {
|
||||
{
|
||||
.name = "NAND FS 0",
|
||||
.offset = 0,
|
||||
.size = 8 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
.name = "NAND FS 1",
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.size = MTDPART_SIZ_FULL
|
||||
},
|
||||
};
|
||||
|
||||
struct platform_nand_data db1550_nand_platdata = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.chip_offset = 0,
|
||||
.nr_partitions = ARRAY_SIZE(db1550_nand_parts),
|
||||
.partitions = db1550_nand_parts,
|
||||
.chip_delay = 20,
|
||||
},
|
||||
.ctrl = {
|
||||
.dev_ready = au1550_nand_device_ready,
|
||||
.cmd_ctrl = au1550_nand_cmd_ctrl,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource db1550_nand_res[] = {
|
||||
[0] = {
|
||||
.start = 0x20000000,
|
||||
.end = 0x200000ff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1550_nand_dev = {
|
||||
.name = "gen_nand",
|
||||
.num_resources = ARRAY_SIZE(db1550_nand_res),
|
||||
.resource = db1550_nand_res,
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &db1550_nand_platdata,
|
||||
}
|
||||
};
|
||||
|
||||
static struct au1550nd_platdata pb1550_nand_pd = {
|
||||
.parts = db1550_nand_parts,
|
||||
.num_parts = ARRAY_SIZE(db1550_nand_parts),
|
||||
.devwidth = 0, /* x8 NAND default, needs fixing up */
|
||||
};
|
||||
|
||||
static struct platform_device pb1550_nand_dev = {
|
||||
.name = "au1550-nand",
|
||||
.id = -1,
|
||||
.resource = db1550_nand_res,
|
||||
.num_resources = ARRAY_SIZE(db1550_nand_res),
|
||||
.dev = {
|
||||
.platform_data = &pb1550_nand_pd,
|
||||
},
|
||||
};
|
||||
|
||||
static void __init pb1550_nand_setup(void)
|
||||
{
|
||||
int boot_swapboot = (alchemy_rdsmem(AU1000_MEM_STSTAT) & (0x7 << 1)) |
|
||||
((bcsr_read(BCSR_STATUS) >> 6) & 0x1);
|
||||
|
||||
gpio_direction_input(206); /* de-assert NAND CS# */
|
||||
switch (boot_swapboot) {
|
||||
case 0: case 2: case 8: case 0xC: case 0xD:
|
||||
/* x16 NAND Flash */
|
||||
pb1550_nand_pd.devwidth = 1;
|
||||
/* fallthrough */
|
||||
case 1: case 3: case 9: case 0xE: case 0xF:
|
||||
/* x8 NAND, already set up */
|
||||
platform_device_register(&pb1550_nand_dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct resource au1550_psc0_res[] = {
|
||||
[0] = {
|
||||
.start = AU1550_PSC0_PHYS_ADDR,
|
||||
.end = AU1550_PSC0_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1550_PSC0_INT,
|
||||
.end = AU1550_PSC0_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1550_DSCR_CMD0_PSC0_TX,
|
||||
.end = AU1550_DSCR_CMD0_PSC0_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1550_DSCR_CMD0_PSC0_RX,
|
||||
.end = AU1550_DSCR_CMD0_PSC0_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static void db1550_spi_cs_en(struct au1550_spi_info *spi, int cs, int pol)
|
||||
{
|
||||
if (cs)
|
||||
bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_SPISEL);
|
||||
else
|
||||
bcsr_mod(BCSR_BOARD, BCSR_BOARD_SPISEL, 0);
|
||||
}
|
||||
|
||||
static struct au1550_spi_info db1550_spi_platdata = {
|
||||
.mainclk_hz = 48000000, /* PSC0 clock: max. 2.4MHz SPI clk */
|
||||
.num_chipselect = 2,
|
||||
.activate_cs = db1550_spi_cs_en,
|
||||
};
|
||||
|
||||
static u64 spi_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device db1550_spi_dev = {
|
||||
.dev = {
|
||||
.dma_mask = &spi_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
.platform_data = &db1550_spi_platdata,
|
||||
},
|
||||
.name = "au1550-spi",
|
||||
.id = 0, /* bus number */
|
||||
.num_resources = ARRAY_SIZE(au1550_psc0_res),
|
||||
.resource = au1550_psc0_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct resource au1550_psc1_res[] = {
|
||||
[0] = {
|
||||
.start = AU1550_PSC1_PHYS_ADDR,
|
||||
.end = AU1550_PSC1_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1550_PSC1_INT,
|
||||
.end = AU1550_PSC1_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1550_DSCR_CMD0_PSC1_TX,
|
||||
.end = AU1550_DSCR_CMD0_PSC1_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1550_DSCR_CMD0_PSC1_RX,
|
||||
.end = AU1550_DSCR_CMD0_PSC1_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1550_ac97_dev = {
|
||||
.name = "au1xpsc_ac97",
|
||||
.id = 1, /* PSC ID */
|
||||
.num_resources = ARRAY_SIZE(au1550_psc1_res),
|
||||
.resource = au1550_psc1_res,
|
||||
};
|
||||
|
||||
|
||||
static struct resource au1550_psc2_res[] = {
|
||||
[0] = {
|
||||
.start = AU1550_PSC2_PHYS_ADDR,
|
||||
.end = AU1550_PSC2_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1550_PSC2_INT,
|
||||
.end = AU1550_PSC2_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1550_DSCR_CMD0_PSC2_TX,
|
||||
.end = AU1550_DSCR_CMD0_PSC2_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1550_DSCR_CMD0_PSC2_RX,
|
||||
.end = AU1550_DSCR_CMD0_PSC2_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1550_i2c_dev = {
|
||||
.name = "au1xpsc_smbus",
|
||||
.id = 0, /* bus number */
|
||||
.num_resources = ARRAY_SIZE(au1550_psc2_res),
|
||||
.resource = au1550_psc2_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct resource au1550_psc3_res[] = {
|
||||
[0] = {
|
||||
.start = AU1550_PSC3_PHYS_ADDR,
|
||||
.end = AU1550_PSC3_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = AU1550_PSC3_INT,
|
||||
.end = AU1550_PSC3_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[2] = {
|
||||
.start = AU1550_DSCR_CMD0_PSC3_TX,
|
||||
.end = AU1550_DSCR_CMD0_PSC3_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[3] = {
|
||||
.start = AU1550_DSCR_CMD0_PSC3_RX,
|
||||
.end = AU1550_DSCR_CMD0_PSC3_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device db1550_i2s_dev = {
|
||||
.name = "au1xpsc_i2s",
|
||||
.id = 3, /* PSC ID */
|
||||
.num_resources = ARRAY_SIZE(au1550_psc3_res),
|
||||
.resource = au1550_psc3_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct platform_device db1550_stac_dev = {
|
||||
.name = "ac97-codec",
|
||||
.id = 1, /* on PSC1 */
|
||||
};
|
||||
|
||||
static struct platform_device db1550_ac97dma_dev = {
|
||||
.name = "au1xpsc-pcm",
|
||||
.id = 1, /* on PSC3 */
|
||||
};
|
||||
|
||||
static struct platform_device db1550_i2sdma_dev = {
|
||||
.name = "au1xpsc-pcm",
|
||||
.id = 3, /* on PSC3 */
|
||||
};
|
||||
|
||||
static struct platform_device db1550_sndac97_dev = {
|
||||
.name = "db1550-ac97",
|
||||
};
|
||||
|
||||
static struct platform_device db1550_sndi2s_dev = {
|
||||
.name = "db1550-i2s",
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static int db1550_map_pci_irq(const struct pci_dev *d, u8 slot, u8 pin)
|
||||
{
|
||||
if ((slot < 11) || (slot > 13) || pin == 0)
|
||||
return -1;
|
||||
if (slot == 11)
|
||||
return (pin == 1) ? AU1550_PCI_INTC : 0xff;
|
||||
if (slot == 12) {
|
||||
switch (pin) {
|
||||
case 1: return AU1550_PCI_INTB;
|
||||
case 2: return AU1550_PCI_INTC;
|
||||
case 3: return AU1550_PCI_INTD;
|
||||
case 4: return AU1550_PCI_INTA;
|
||||
}
|
||||
}
|
||||
if (slot == 13) {
|
||||
switch (pin) {
|
||||
case 1: return AU1550_PCI_INTA;
|
||||
case 2: return AU1550_PCI_INTB;
|
||||
case 3: return AU1550_PCI_INTC;
|
||||
case 4: return AU1550_PCI_INTD;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pb1550_map_pci_irq(const struct pci_dev *d, u8 slot, u8 pin)
|
||||
{
|
||||
if ((slot < 12) || (slot > 13) || pin == 0)
|
||||
return -1;
|
||||
if (slot == 12) {
|
||||
switch (pin) {
|
||||
case 1: return AU1500_PCI_INTB;
|
||||
case 2: return AU1500_PCI_INTC;
|
||||
case 3: return AU1500_PCI_INTD;
|
||||
case 4: return AU1500_PCI_INTA;
|
||||
}
|
||||
}
|
||||
if (slot == 13) {
|
||||
switch (pin) {
|
||||
case 1: return AU1500_PCI_INTA;
|
||||
case 2: return AU1500_PCI_INTB;
|
||||
case 3: return AU1500_PCI_INTC;
|
||||
case 4: return AU1500_PCI_INTD;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct resource alchemy_pci_host_res[] = {
|
||||
[0] = {
|
||||
.start = AU1500_PCI_PHYS_ADDR,
|
||||
.end = AU1500_PCI_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct alchemy_pci_platdata db1550_pci_pd = {
|
||||
.board_map_irq = db1550_map_pci_irq,
|
||||
};
|
||||
|
||||
static struct platform_device db1550_pci_host_dev = {
|
||||
.dev.platform_data = &db1550_pci_pd,
|
||||
.name = "alchemy-pci",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(alchemy_pci_host_res),
|
||||
.resource = alchemy_pci_host_res,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
static struct platform_device *db1550_devs[] __initdata = {
|
||||
&db1550_i2c_dev,
|
||||
&db1550_ac97_dev,
|
||||
&db1550_spi_dev,
|
||||
&db1550_i2s_dev,
|
||||
&db1550_stac_dev,
|
||||
&db1550_ac97dma_dev,
|
||||
&db1550_i2sdma_dev,
|
||||
&db1550_sndac97_dev,
|
||||
&db1550_sndi2s_dev,
|
||||
};
|
||||
|
||||
/* must be arch_initcall; MIPS PCI scans busses in a subsys_initcall */
|
||||
int __init db1550_pci_setup(int id)
|
||||
{
|
||||
if (id)
|
||||
db1550_pci_pd.board_map_irq = pb1550_map_pci_irq;
|
||||
return platform_device_register(&db1550_pci_host_dev);
|
||||
}
|
||||
|
||||
static void __init db1550_devices(void)
|
||||
{
|
||||
alchemy_gpio_direction_output(203, 0); /* red led on */
|
||||
|
||||
irq_set_irq_type(AU1550_GPIO0_INT, IRQ_TYPE_EDGE_BOTH); /* CD0# */
|
||||
irq_set_irq_type(AU1550_GPIO1_INT, IRQ_TYPE_EDGE_BOTH); /* CD1# */
|
||||
irq_set_irq_type(AU1550_GPIO3_INT, IRQ_TYPE_LEVEL_LOW); /* CARD0# */
|
||||
irq_set_irq_type(AU1550_GPIO5_INT, IRQ_TYPE_LEVEL_LOW); /* CARD1# */
|
||||
irq_set_irq_type(AU1550_GPIO21_INT, IRQ_TYPE_LEVEL_LOW); /* STSCHG0# */
|
||||
irq_set_irq_type(AU1550_GPIO22_INT, IRQ_TYPE_LEVEL_LOW); /* STSCHG1# */
|
||||
|
||||
db1x_register_pcmcia_socket(
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR,
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x000400000 - 1,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x000010000 - 1,
|
||||
AU1550_GPIO3_INT, AU1550_GPIO0_INT,
|
||||
/*AU1550_GPIO21_INT*/0, 0, 0);
|
||||
|
||||
db1x_register_pcmcia_socket(
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004000000,
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004400000 - 1,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004000000,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004400000 - 1,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x004000000,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x004010000 - 1,
|
||||
AU1550_GPIO5_INT, AU1550_GPIO1_INT,
|
||||
/*AU1550_GPIO22_INT*/0, 0, 1);
|
||||
|
||||
platform_device_register(&db1550_nand_dev);
|
||||
|
||||
alchemy_gpio_direction_output(202, 0); /* green led on */
|
||||
}
|
||||
|
||||
static void __init pb1550_devices(void)
|
||||
{
|
||||
irq_set_irq_type(AU1550_GPIO0_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1550_GPIO1_INT, IRQ_TYPE_LEVEL_LOW);
|
||||
irq_set_irq_type(AU1550_GPIO201_205_INT, IRQ_TYPE_LEVEL_HIGH);
|
||||
|
||||
/* enable both PCMCIA card irqs in the shared line */
|
||||
alchemy_gpio2_enable_int(201); /* socket 0 card irq */
|
||||
alchemy_gpio2_enable_int(202); /* socket 1 card irq */
|
||||
|
||||
/* Pb1550, like all others, also has statuschange irqs; however they're
|
||||
* wired up on one of the Au1550's shared GPIO201_205 line, which also
|
||||
* services the PCMCIA card interrupts. So we ignore statuschange and
|
||||
* use the GPIO201_205 exclusively for card interrupts, since a) pcmcia
|
||||
* drivers are used to shared irqs and b) statuschange isn't really use-
|
||||
* ful anyway.
|
||||
*/
|
||||
db1x_register_pcmcia_socket(
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR,
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x000400000 - 1,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x000010000 - 1,
|
||||
AU1550_GPIO201_205_INT, AU1550_GPIO0_INT, 0, 0, 0);
|
||||
|
||||
db1x_register_pcmcia_socket(
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x008000000,
|
||||
AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x008400000 - 1,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x008000000,
|
||||
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x008400000 - 1,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x008000000,
|
||||
AU1000_PCMCIA_IO_PHYS_ADDR + 0x008010000 - 1,
|
||||
AU1550_GPIO201_205_INT, AU1550_GPIO1_INT, 0, 0, 1);
|
||||
|
||||
pb1550_nand_setup();
|
||||
}
|
||||
|
||||
int __init db1550_dev_setup(void)
|
||||
{
|
||||
int swapped, id;
|
||||
struct clk *c;
|
||||
|
||||
id = (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)) != BCSR_WHOAMI_DB1550);
|
||||
|
||||
i2c_register_board_info(0, db1550_i2c_devs,
|
||||
ARRAY_SIZE(db1550_i2c_devs));
|
||||
spi_register_board_info(db1550_spi_devs,
|
||||
ARRAY_SIZE(db1550_i2c_devs));
|
||||
|
||||
c = clk_get(NULL, "psc0_intclk");
|
||||
if (!IS_ERR(c)) {
|
||||
clk_set_rate(c, 50000000);
|
||||
clk_prepare_enable(c);
|
||||
clk_put(c);
|
||||
}
|
||||
c = clk_get(NULL, "psc2_intclk");
|
||||
if (!IS_ERR(c)) {
|
||||
clk_set_rate(c, db1550_spi_platdata.mainclk_hz);
|
||||
clk_prepare_enable(c);
|
||||
clk_put(c);
|
||||
}
|
||||
|
||||
/* Audio PSC clock is supplied by codecs (PSC1, 3) FIXME: platdata!! */
|
||||
__raw_writel(PSC_SEL_CLK_SERCLK,
|
||||
(void __iomem *)KSEG1ADDR(AU1550_PSC1_PHYS_ADDR) + PSC_SEL_OFFSET);
|
||||
wmb();
|
||||
__raw_writel(PSC_SEL_CLK_SERCLK,
|
||||
(void __iomem *)KSEG1ADDR(AU1550_PSC3_PHYS_ADDR) + PSC_SEL_OFFSET);
|
||||
wmb();
|
||||
/* SPI/I2C use internally supplied 50MHz source */
|
||||
__raw_writel(PSC_SEL_CLK_INTCLK,
|
||||
(void __iomem *)KSEG1ADDR(AU1550_PSC0_PHYS_ADDR) + PSC_SEL_OFFSET);
|
||||
wmb();
|
||||
__raw_writel(PSC_SEL_CLK_INTCLK,
|
||||
(void __iomem *)KSEG1ADDR(AU1550_PSC2_PHYS_ADDR) + PSC_SEL_OFFSET);
|
||||
wmb();
|
||||
|
||||
id ? pb1550_devices() : db1550_devices();
|
||||
|
||||
swapped = bcsr_read(BCSR_STATUS) &
|
||||
(id ? BCSR_STATUS_PB1550_SWAPBOOT : BCSR_STATUS_DB1000_SWAPBOOT);
|
||||
db1x_register_norflash(128 << 20, 4, swapped);
|
||||
|
||||
return platform_add_devices(db1550_devs, ARRAY_SIZE(db1550_devs));
|
||||
}
|
121
arch/mips/alchemy/devboards/db1xxx.c
Normal file
121
arch/mips/alchemy/devboards/db1xxx.c
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Alchemy DB/PB1xxx board support.
|
||||
*/
|
||||
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-db1x00/bcsr.h>
|
||||
|
||||
int __init db1000_board_setup(void);
|
||||
int __init db1000_dev_setup(void);
|
||||
int __init db1500_pci_setup(void);
|
||||
int __init db1200_board_setup(void);
|
||||
int __init db1200_dev_setup(void);
|
||||
int __init db1300_board_setup(void);
|
||||
int __init db1300_dev_setup(void);
|
||||
int __init db1550_board_setup(void);
|
||||
int __init db1550_dev_setup(void);
|
||||
int __init db1550_pci_setup(int);
|
||||
|
||||
static const char *board_type_str(void)
|
||||
{
|
||||
switch (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI))) {
|
||||
case BCSR_WHOAMI_DB1000:
|
||||
return "DB1000";
|
||||
case BCSR_WHOAMI_DB1500:
|
||||
return "DB1500";
|
||||
case BCSR_WHOAMI_DB1100:
|
||||
return "DB1100";
|
||||
case BCSR_WHOAMI_PB1500:
|
||||
case BCSR_WHOAMI_PB1500R2:
|
||||
return "PB1500";
|
||||
case BCSR_WHOAMI_PB1100:
|
||||
return "PB1100";
|
||||
case BCSR_WHOAMI_PB1200_DDR1:
|
||||
case BCSR_WHOAMI_PB1200_DDR2:
|
||||
return "PB1200";
|
||||
case BCSR_WHOAMI_DB1200:
|
||||
return "DB1200";
|
||||
case BCSR_WHOAMI_DB1300:
|
||||
return "DB1300";
|
||||
case BCSR_WHOAMI_DB1550:
|
||||
return "DB1550";
|
||||
case BCSR_WHOAMI_PB1550_SDR:
|
||||
case BCSR_WHOAMI_PB1550_DDR:
|
||||
return "PB1550";
|
||||
default:
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
|
||||
const char *get_system_type(void)
|
||||
{
|
||||
return board_type_str();
|
||||
}
|
||||
|
||||
void __init board_setup(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (alchemy_get_cputype()) {
|
||||
case ALCHEMY_CPU_AU1000:
|
||||
case ALCHEMY_CPU_AU1500:
|
||||
case ALCHEMY_CPU_AU1100:
|
||||
ret = db1000_board_setup();
|
||||
break;
|
||||
case ALCHEMY_CPU_AU1550:
|
||||
ret = db1550_board_setup();
|
||||
break;
|
||||
case ALCHEMY_CPU_AU1200:
|
||||
ret = db1200_board_setup();
|
||||
break;
|
||||
case ALCHEMY_CPU_AU1300:
|
||||
ret = db1300_board_setup();
|
||||
break;
|
||||
default:
|
||||
pr_err("unsupported CPU on board\n");
|
||||
ret = -ENODEV;
|
||||
}
|
||||
if (ret)
|
||||
panic("cannot initialize board support");
|
||||
}
|
||||
|
||||
static int __init db1xxx_arch_init(void)
|
||||
{
|
||||
int id = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI));
|
||||
if (id == BCSR_WHOAMI_DB1550)
|
||||
return db1550_pci_setup(0);
|
||||
else if ((id == BCSR_WHOAMI_PB1550_SDR) ||
|
||||
(id == BCSR_WHOAMI_PB1550_DDR))
|
||||
return db1550_pci_setup(1);
|
||||
else if ((id == BCSR_WHOAMI_DB1500) || (id == BCSR_WHOAMI_PB1500) ||
|
||||
(id == BCSR_WHOAMI_PB1500R2))
|
||||
return db1500_pci_setup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(db1xxx_arch_init);
|
||||
|
||||
static int __init db1xxx_dev_init(void)
|
||||
{
|
||||
switch (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI))) {
|
||||
case BCSR_WHOAMI_DB1000:
|
||||
case BCSR_WHOAMI_DB1500:
|
||||
case BCSR_WHOAMI_DB1100:
|
||||
case BCSR_WHOAMI_PB1500:
|
||||
case BCSR_WHOAMI_PB1500R2:
|
||||
case BCSR_WHOAMI_PB1100:
|
||||
return db1000_dev_setup();
|
||||
case BCSR_WHOAMI_PB1200_DDR1:
|
||||
case BCSR_WHOAMI_PB1200_DDR2:
|
||||
case BCSR_WHOAMI_DB1200:
|
||||
return db1200_dev_setup();
|
||||
case BCSR_WHOAMI_DB1300:
|
||||
return db1300_dev_setup();
|
||||
case BCSR_WHOAMI_DB1550:
|
||||
case BCSR_WHOAMI_PB1550_SDR:
|
||||
case BCSR_WHOAMI_PB1550_DDR:
|
||||
return db1550_dev_setup();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
device_initcall(db1xxx_dev_init);
|
263
arch/mips/alchemy/devboards/platform.c
Normal file
263
arch/mips/alchemy/devboards/platform.c
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* devoard misc stuff.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/idle.h>
|
||||
#include <asm/reboot.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-db1x00/bcsr.h>
|
||||
|
||||
#include <prom.h>
|
||||
|
||||
void __init prom_init(void)
|
||||
{
|
||||
unsigned char *memsize_str;
|
||||
unsigned long memsize;
|
||||
|
||||
prom_argc = (int)fw_arg0;
|
||||
prom_argv = (char **)fw_arg1;
|
||||
prom_envp = (char **)fw_arg2;
|
||||
|
||||
prom_init_cmdline();
|
||||
memsize_str = prom_getenv("memsize");
|
||||
if (!memsize_str || kstrtoul(memsize_str, 0, &memsize))
|
||||
memsize = 64 << 20; /* all devboards have at least 64MB RAM */
|
||||
|
||||
add_memory_region(0, memsize, BOOT_MEM_RAM);
|
||||
}
|
||||
|
||||
void prom_putchar(unsigned char c)
|
||||
{
|
||||
if (alchemy_get_cputype() == ALCHEMY_CPU_AU1300)
|
||||
alchemy_uart_putchar(AU1300_UART2_PHYS_ADDR, c);
|
||||
else
|
||||
alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c);
|
||||
}
|
||||
|
||||
|
||||
static struct platform_device db1x00_rtc_dev = {
|
||||
.name = "rtc-au1xxx",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
|
||||
static void db1x_power_off(void)
|
||||
{
|
||||
bcsr_write(BCSR_RESETS, 0);
|
||||
bcsr_write(BCSR_SYSTEM, BCSR_SYSTEM_PWROFF | BCSR_SYSTEM_RESET);
|
||||
while (1) /* sit and spin */
|
||||
cpu_wait();
|
||||
}
|
||||
|
||||
static void db1x_reset(char *c)
|
||||
{
|
||||
bcsr_write(BCSR_RESETS, 0);
|
||||
bcsr_write(BCSR_SYSTEM, 0);
|
||||
}
|
||||
|
||||
static int __init db1x_late_setup(void)
|
||||
{
|
||||
if (!pm_power_off)
|
||||
pm_power_off = db1x_power_off;
|
||||
if (!_machine_halt)
|
||||
_machine_halt = db1x_power_off;
|
||||
if (!_machine_restart)
|
||||
_machine_restart = db1x_reset;
|
||||
|
||||
platform_device_register(&db1x00_rtc_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(db1x_late_setup);
|
||||
|
||||
/* register a pcmcia socket */
|
||||
int __init db1x_register_pcmcia_socket(phys_addr_t pcmcia_attr_start,
|
||||
phys_addr_t pcmcia_attr_end,
|
||||
phys_addr_t pcmcia_mem_start,
|
||||
phys_addr_t pcmcia_mem_end,
|
||||
phys_addr_t pcmcia_io_start,
|
||||
phys_addr_t pcmcia_io_end,
|
||||
int card_irq,
|
||||
int cd_irq,
|
||||
int stschg_irq,
|
||||
int eject_irq,
|
||||
int id)
|
||||
{
|
||||
int cnt, i, ret;
|
||||
struct resource *sr;
|
||||
struct platform_device *pd;
|
||||
|
||||
cnt = 5;
|
||||
if (eject_irq)
|
||||
cnt++;
|
||||
if (stschg_irq)
|
||||
cnt++;
|
||||
|
||||
sr = kzalloc(sizeof(struct resource) * cnt, GFP_KERNEL);
|
||||
if (!sr)
|
||||
return -ENOMEM;
|
||||
|
||||
pd = platform_device_alloc("db1xxx_pcmcia", id);
|
||||
if (!pd) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sr[0].name = "pcmcia-attr";
|
||||
sr[0].flags = IORESOURCE_MEM;
|
||||
sr[0].start = pcmcia_attr_start;
|
||||
sr[0].end = pcmcia_attr_end;
|
||||
|
||||
sr[1].name = "pcmcia-mem";
|
||||
sr[1].flags = IORESOURCE_MEM;
|
||||
sr[1].start = pcmcia_mem_start;
|
||||
sr[1].end = pcmcia_mem_end;
|
||||
|
||||
sr[2].name = "pcmcia-io";
|
||||
sr[2].flags = IORESOURCE_MEM;
|
||||
sr[2].start = pcmcia_io_start;
|
||||
sr[2].end = pcmcia_io_end;
|
||||
|
||||
sr[3].name = "insert";
|
||||
sr[3].flags = IORESOURCE_IRQ;
|
||||
sr[3].start = sr[3].end = cd_irq;
|
||||
|
||||
sr[4].name = "card";
|
||||
sr[4].flags = IORESOURCE_IRQ;
|
||||
sr[4].start = sr[4].end = card_irq;
|
||||
|
||||
i = 5;
|
||||
if (stschg_irq) {
|
||||
sr[i].name = "stschg";
|
||||
sr[i].flags = IORESOURCE_IRQ;
|
||||
sr[i].start = sr[i].end = stschg_irq;
|
||||
i++;
|
||||
}
|
||||
if (eject_irq) {
|
||||
sr[i].name = "eject";
|
||||
sr[i].flags = IORESOURCE_IRQ;
|
||||
sr[i].start = sr[i].end = eject_irq;
|
||||
}
|
||||
|
||||
pd->resource = sr;
|
||||
pd->num_resources = cnt;
|
||||
|
||||
ret = platform_device_add(pd);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
platform_device_put(pd);
|
||||
out:
|
||||
kfree(sr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define YAMON_SIZE 0x00100000
|
||||
#define YAMON_ENV_SIZE 0x00040000
|
||||
|
||||
int __init db1x_register_norflash(unsigned long size, int width,
|
||||
int swapped)
|
||||
{
|
||||
struct physmap_flash_data *pfd;
|
||||
struct platform_device *pd;
|
||||
struct mtd_partition *parts;
|
||||
struct resource *res;
|
||||
int ret, i;
|
||||
|
||||
if (size < (8 * 1024 * 1024))
|
||||
return -EINVAL;
|
||||
|
||||
ret = -ENOMEM;
|
||||
parts = kzalloc(sizeof(struct mtd_partition) * 5, GFP_KERNEL);
|
||||
if (!parts)
|
||||
goto out;
|
||||
|
||||
res = kzalloc(sizeof(struct resource), GFP_KERNEL);
|
||||
if (!res)
|
||||
goto out1;
|
||||
|
||||
pfd = kzalloc(sizeof(struct physmap_flash_data), GFP_KERNEL);
|
||||
if (!pfd)
|
||||
goto out2;
|
||||
|
||||
pd = platform_device_alloc("physmap-flash", 0);
|
||||
if (!pd)
|
||||
goto out3;
|
||||
|
||||
/* NOR flash ends at 0x20000000, regardless of size */
|
||||
res->start = 0x20000000 - size;
|
||||
res->end = 0x20000000 - 1;
|
||||
res->flags = IORESOURCE_MEM;
|
||||
|
||||
/* partition setup. Most Develboards have a switch which allows
|
||||
* to swap the physical locations of the 2 NOR flash banks.
|
||||
*/
|
||||
i = 0;
|
||||
if (!swapped) {
|
||||
/* first NOR chip */
|
||||
parts[i].offset = 0;
|
||||
parts[i].name = "User FS";
|
||||
parts[i].size = size / 2;
|
||||
i++;
|
||||
}
|
||||
|
||||
parts[i].offset = MTDPART_OFS_APPEND;
|
||||
parts[i].name = "User FS 2";
|
||||
parts[i].size = (size / 2) - (0x20000000 - 0x1fc00000);
|
||||
i++;
|
||||
|
||||
parts[i].offset = MTDPART_OFS_APPEND;
|
||||
parts[i].name = "YAMON";
|
||||
parts[i].size = YAMON_SIZE;
|
||||
parts[i].mask_flags = MTD_WRITEABLE;
|
||||
i++;
|
||||
|
||||
parts[i].offset = MTDPART_OFS_APPEND;
|
||||
parts[i].name = "raw kernel";
|
||||
parts[i].size = 0x00400000 - YAMON_SIZE - YAMON_ENV_SIZE;
|
||||
i++;
|
||||
|
||||
parts[i].offset = MTDPART_OFS_APPEND;
|
||||
parts[i].name = "YAMON Env";
|
||||
parts[i].size = YAMON_ENV_SIZE;
|
||||
parts[i].mask_flags = MTD_WRITEABLE;
|
||||
i++;
|
||||
|
||||
if (swapped) {
|
||||
parts[i].offset = MTDPART_OFS_APPEND;
|
||||
parts[i].name = "User FS";
|
||||
parts[i].size = size / 2;
|
||||
i++;
|
||||
}
|
||||
|
||||
pfd->width = width;
|
||||
pfd->parts = parts;
|
||||
pfd->nr_parts = 5;
|
||||
|
||||
pd->dev.platform_data = pfd;
|
||||
pd->resource = res;
|
||||
pd->num_resources = 1;
|
||||
|
||||
ret = platform_device_add(pd);
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
platform_device_put(pd);
|
||||
out3:
|
||||
kfree(pfd);
|
||||
out2:
|
||||
kfree(res);
|
||||
out1:
|
||||
kfree(parts);
|
||||
out:
|
||||
return ret;
|
||||
}
|
21
arch/mips/alchemy/devboards/platform.h
Normal file
21
arch/mips/alchemy/devboards/platform.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef _DEVBOARD_PLATFORM_H_
|
||||
#define _DEVBOARD_PLATFORM_H_
|
||||
|
||||
#include <linux/init.h>
|
||||
|
||||
int __init db1x_register_pcmcia_socket(phys_addr_t pcmcia_attr_start,
|
||||
phys_addr_t pcmcia_attr_len,
|
||||
phys_addr_t pcmcia_mem_start,
|
||||
phys_addr_t pcmcia_mem_end,
|
||||
phys_addr_t pcmcia_io_start,
|
||||
phys_addr_t pcmcia_io_end,
|
||||
int card_irq,
|
||||
int cd_irq,
|
||||
int stschg_irq,
|
||||
int eject_irq,
|
||||
int id);
|
||||
|
||||
int __init db1x_register_norflash(unsigned long size, int width,
|
||||
int swapped);
|
||||
|
||||
#endif
|
253
arch/mips/alchemy/devboards/pm.c
Normal file
253
arch/mips/alchemy/devboards/pm.c
Normal file
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* Alchemy Development Board example suspend userspace interface.
|
||||
*
|
||||
* (c) 2008 Manuel Lauss <mano@roarinelk.homelinux.net>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-au1x00/gpio.h>
|
||||
#include <asm/mach-db1x00/bcsr.h>
|
||||
|
||||
/*
|
||||
* Generic suspend userspace interface for Alchemy development boards.
|
||||
* This code exports a few sysfs nodes under /sys/power/db1x/ which
|
||||
* can be used by userspace to en/disable all au1x-provided wakeup
|
||||
* sources and configure the timeout after which the the TOYMATCH2 irq
|
||||
* is to trigger a wakeup.
|
||||
*/
|
||||
|
||||
|
||||
static unsigned long db1x_pm_sleep_secs;
|
||||
static unsigned long db1x_pm_wakemsk;
|
||||
static unsigned long db1x_pm_last_wakesrc;
|
||||
|
||||
static int db1x_pm_enter(suspend_state_t state)
|
||||
{
|
||||
unsigned short bcsrs[16];
|
||||
int i, j, hasint;
|
||||
|
||||
/* save CPLD regs */
|
||||
hasint = bcsr_read(BCSR_WHOAMI);
|
||||
hasint = BCSR_WHOAMI_BOARD(hasint) >= BCSR_WHOAMI_DB1200;
|
||||
j = (hasint) ? BCSR_MASKSET : BCSR_SYSTEM;
|
||||
|
||||
for (i = BCSR_STATUS; i <= j; i++)
|
||||
bcsrs[i] = bcsr_read(i);
|
||||
|
||||
/* shut off hexleds */
|
||||
bcsr_write(BCSR_HEXCLEAR, 3);
|
||||
|
||||
/* enable GPIO based wakeup */
|
||||
alchemy_gpio1_input_enable();
|
||||
|
||||
/* clear and setup wake cause and source */
|
||||
alchemy_wrsys(0, AU1000_SYS_WAKEMSK);
|
||||
alchemy_wrsys(0, AU1000_SYS_WAKESRC);
|
||||
|
||||
alchemy_wrsys(db1x_pm_wakemsk, AU1000_SYS_WAKEMSK);
|
||||
|
||||
/* setup 1Hz-timer-based wakeup: wait for reg access */
|
||||
while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M20)
|
||||
asm volatile ("nop");
|
||||
|
||||
alchemy_wrsys(alchemy_rdsys(AU1000_SYS_TOYREAD) + db1x_pm_sleep_secs,
|
||||
AU1000_SYS_TOYMATCH2);
|
||||
|
||||
/* wait for value to really hit the register */
|
||||
while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M20)
|
||||
asm volatile ("nop");
|
||||
|
||||
/* ...and now the sandman can come! */
|
||||
au_sleep();
|
||||
|
||||
|
||||
/* restore CPLD regs */
|
||||
for (i = BCSR_STATUS; i <= BCSR_SYSTEM; i++)
|
||||
bcsr_write(i, bcsrs[i]);
|
||||
|
||||
/* restore CPLD int registers */
|
||||
if (hasint) {
|
||||
bcsr_write(BCSR_INTCLR, 0xffff);
|
||||
bcsr_write(BCSR_MASKCLR, 0xffff);
|
||||
bcsr_write(BCSR_INTSTAT, 0xffff);
|
||||
bcsr_write(BCSR_INTSET, bcsrs[BCSR_INTSET]);
|
||||
bcsr_write(BCSR_MASKSET, bcsrs[BCSR_MASKSET]);
|
||||
}
|
||||
|
||||
/* light up hexleds */
|
||||
bcsr_write(BCSR_HEXCLEAR, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int db1x_pm_begin(suspend_state_t state)
|
||||
{
|
||||
if (!db1x_pm_wakemsk) {
|
||||
printk(KERN_ERR "db1x: no wakeup source activated!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void db1x_pm_end(void)
|
||||
{
|
||||
/* read and store wakeup source, the clear the register. To
|
||||
* be able to clear it, WAKEMSK must be cleared first.
|
||||
*/
|
||||
db1x_pm_last_wakesrc = alchemy_rdsys(AU1000_SYS_WAKESRC);
|
||||
|
||||
alchemy_wrsys(0, AU1000_SYS_WAKEMSK);
|
||||
alchemy_wrsys(0, AU1000_SYS_WAKESRC);
|
||||
}
|
||||
|
||||
static const struct platform_suspend_ops db1x_pm_ops = {
|
||||
.valid = suspend_valid_only_mem,
|
||||
.begin = db1x_pm_begin,
|
||||
.enter = db1x_pm_enter,
|
||||
.end = db1x_pm_end,
|
||||
};
|
||||
|
||||
#define ATTRCMP(x) (0 == strcmp(attr->attr.name, #x))
|
||||
|
||||
static ssize_t db1x_pmattr_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (ATTRCMP(timer_timeout))
|
||||
return sprintf(buf, "%lu\n", db1x_pm_sleep_secs);
|
||||
|
||||
else if (ATTRCMP(timer))
|
||||
return sprintf(buf, "%u\n",
|
||||
!!(db1x_pm_wakemsk & SYS_WAKEMSK_M2));
|
||||
|
||||
else if (ATTRCMP(wakesrc))
|
||||
return sprintf(buf, "%lu\n", db1x_pm_last_wakesrc);
|
||||
|
||||
else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
|
||||
ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
|
||||
ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
|
||||
idx = (attr->attr.name)[4] - '0';
|
||||
return sprintf(buf, "%d\n",
|
||||
!!(db1x_pm_wakemsk & SYS_WAKEMSK_GPIO(idx)));
|
||||
|
||||
} else if (ATTRCMP(wakemsk)) {
|
||||
return sprintf(buf, "%08lx\n", db1x_pm_wakemsk);
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static ssize_t db1x_pmattr_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *instr,
|
||||
size_t bytes)
|
||||
{
|
||||
unsigned long l;
|
||||
int tmp;
|
||||
|
||||
if (ATTRCMP(timer_timeout)) {
|
||||
tmp = kstrtoul(instr, 0, &l);
|
||||
if (tmp)
|
||||
return tmp;
|
||||
|
||||
db1x_pm_sleep_secs = l;
|
||||
|
||||
} else if (ATTRCMP(timer)) {
|
||||
if (instr[0] != '0')
|
||||
db1x_pm_wakemsk |= SYS_WAKEMSK_M2;
|
||||
else
|
||||
db1x_pm_wakemsk &= ~SYS_WAKEMSK_M2;
|
||||
|
||||
} else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
|
||||
ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
|
||||
ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
|
||||
tmp = (attr->attr.name)[4] - '0';
|
||||
if (instr[0] != '0') {
|
||||
db1x_pm_wakemsk |= SYS_WAKEMSK_GPIO(tmp);
|
||||
} else {
|
||||
db1x_pm_wakemsk &= ~SYS_WAKEMSK_GPIO(tmp);
|
||||
}
|
||||
|
||||
} else if (ATTRCMP(wakemsk)) {
|
||||
tmp = kstrtoul(instr, 0, &l);
|
||||
if (tmp)
|
||||
return tmp;
|
||||
|
||||
db1x_pm_wakemsk = l & 0x0000003f;
|
||||
|
||||
} else
|
||||
bytes = -ENOENT;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#define ATTR(x) \
|
||||
static struct kobj_attribute x##_attribute = \
|
||||
__ATTR(x, 0664, db1x_pmattr_show, \
|
||||
db1x_pmattr_store);
|
||||
|
||||
ATTR(gpio0) /* GPIO-based wakeup enable */
|
||||
ATTR(gpio1)
|
||||
ATTR(gpio2)
|
||||
ATTR(gpio3)
|
||||
ATTR(gpio4)
|
||||
ATTR(gpio5)
|
||||
ATTR(gpio6)
|
||||
ATTR(gpio7)
|
||||
ATTR(timer) /* TOYMATCH2-based wakeup enable */
|
||||
ATTR(timer_timeout) /* timer-based wakeup timeout value, in seconds */
|
||||
ATTR(wakesrc) /* contents of SYS_WAKESRC after last wakeup */
|
||||
ATTR(wakemsk) /* direct access to SYS_WAKEMSK */
|
||||
|
||||
#define ATTR_LIST(x) & x ## _attribute.attr
|
||||
static struct attribute *db1x_pmattrs[] = {
|
||||
ATTR_LIST(gpio0),
|
||||
ATTR_LIST(gpio1),
|
||||
ATTR_LIST(gpio2),
|
||||
ATTR_LIST(gpio3),
|
||||
ATTR_LIST(gpio4),
|
||||
ATTR_LIST(gpio5),
|
||||
ATTR_LIST(gpio6),
|
||||
ATTR_LIST(gpio7),
|
||||
ATTR_LIST(timer),
|
||||
ATTR_LIST(timer_timeout),
|
||||
ATTR_LIST(wakesrc),
|
||||
ATTR_LIST(wakemsk),
|
||||
NULL, /* terminator */
|
||||
};
|
||||
|
||||
static struct attribute_group db1x_pmattr_group = {
|
||||
.name = "db1x",
|
||||
.attrs = db1x_pmattrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize suspend interface
|
||||
*/
|
||||
static int __init pm_init(void)
|
||||
{
|
||||
/* init TOY to tick at 1Hz if not already done. No need to wait
|
||||
* for confirmation since there's plenty of time from here to
|
||||
* the next suspend cycle.
|
||||
*/
|
||||
if (alchemy_rdsys(AU1000_SYS_TOYTRIM) != 32767)
|
||||
alchemy_wrsys(32767, AU1000_SYS_TOYTRIM);
|
||||
|
||||
db1x_pm_last_wakesrc = alchemy_rdsys(AU1000_SYS_WAKESRC);
|
||||
|
||||
alchemy_wrsys(0, AU1000_SYS_WAKESRC);
|
||||
alchemy_wrsys(0, AU1000_SYS_WAKEMSK);
|
||||
|
||||
suspend_set_ops(&db1x_pm_ops);
|
||||
|
||||
return sysfs_create_group(power_kobj, &db1x_pmattr_group);
|
||||
}
|
||||
|
||||
late_initcall(pm_init);
|
Loading…
Add table
Add a link
Reference in a new issue