diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
commit | 76cb841cb886eef6b3bee341a2266c76578724ad (patch) | |
tree | f5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /arch/mips/alchemy/devboards | |
parent | Initial commit. (diff) | |
download | linux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip |
Adding upstream version 4.19.249.upstream/4.19.249
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/mips/alchemy/devboards')
-rw-r--r-- | arch/mips/alchemy/devboards/Makefile | 6 | ||||
-rw-r--r-- | arch/mips/alchemy/devboards/bcsr.c | 146 | ||||
-rw-r--r-- | arch/mips/alchemy/devboards/db1000.c | 631 | ||||
-rw-r--r-- | arch/mips/alchemy/devboards/db1200.c | 969 | ||||
-rw-r--r-- | arch/mips/alchemy/devboards/db1300.c | 872 | ||||
-rw-r--r-- | arch/mips/alchemy/devboards/db1550.c | 621 | ||||
-rw-r--r-- | arch/mips/alchemy/devboards/db1xxx.c | 124 | ||||
-rw-r--r-- | arch/mips/alchemy/devboards/platform.c | 265 | ||||
-rw-r--r-- | arch/mips/alchemy/devboards/platform.h | 22 | ||||
-rw-r--r-- | arch/mips/alchemy/devboards/pm.c | 254 |
10 files changed, 3910 insertions, 0 deletions
diff --git a/arch/mips/alchemy/devboards/Makefile b/arch/mips/alchemy/devboards/Makefile new file mode 100644 index 000000000..9da3659a9 --- /dev/null +++ b/arch/mips/alchemy/devboards/Makefile @@ -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 diff --git a/arch/mips/alchemy/devboards/bcsr.c b/arch/mips/alchemy/devboards/bcsr.c new file mode 100644 index 000000000..c1a2daaf3 --- /dev/null +++ b/arch/mips/alchemy/devboards/bcsr.c @@ -0,0 +1,146 @@ +/* + * 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/irqchip/chained_irq.h> +#include <linux/init.h> +#include <linux/export.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(struct irq_desc *d) +{ + unsigned short bisr = __raw_readw(bcsr_virt + BCSR_REG_INTSTAT); + struct irq_chip *chip = irq_desc_get_chip(d); + + chained_irq_enter(chip, d); + generic_handle_irq(bcsr_csc_base + __ffs(bisr)); + chained_irq_exit(chip, d); +} + +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); +} diff --git a/arch/mips/alchemy/devboards/db1000.c b/arch/mips/alchemy/devboards/db1000.c new file mode 100644 index 000000000..13e3c8485 --- /dev/null +++ b/arch/mips/alchemy/devboards/db1000.c @@ -0,0 +1,631 @@ +/* + * 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/gpio/machine.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/gpio-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 = { + .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, + }, +}; + +static struct platform_device db1100_spi_dev = { + .name = "spi_gpio", + .id = 0, + .dev = { + .platform_data = &db1100_spictl_pd, + }, +}; + +/* + * Alchemy GPIO 2 has its base at 200 so the GPIO lines + * 207 thru 210 are GPIOs at offset 7 thru 10 at this chip. + */ +static struct gpiod_lookup_table db1100_spi_gpiod_table = { + .dev_id = "spi_gpio", + .table = { + GPIO_LOOKUP("alchemy-gpio2", 9, + "sck", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("alchemy-gpio2", 8, + "mosi", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("alchemy-gpio2", 7, + "miso", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("alchemy-gpio2", 10, + "cs", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +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 = 0; /* GPIO number, NOT irq! */ + d1 = 3; /* GPIO number, NOT irq! */ + s0 = AU1500_GPIO1_INT; + s1 = AU1500_GPIO4_INT; + } else if (board == BCSR_WHOAMI_DB1100) { + c0 = AU1100_GPIO2_INT; + c1 = AU1100_GPIO5_INT; + d0 = 0; /* GPIO number, NOT irq! */ + d1 = 3; /* GPIO number, NOT irq! */ + 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)); + gpiod_add_lookup_table(&db1100_spi_gpiod_table); + platform_device_register(&db1100_spi_dev); + } else if (board == BCSR_WHOAMI_DB1000) { + c0 = AU1000_GPIO2_INT; + c1 = AU1000_GPIO5_INT; + d0 = 0; /* GPIO number, NOT irq! */ + d1 = 3; /* GPIO number, NOT irq! */ + 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 = 1; /* GPIO number, NOT irq! */ + 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 = 9; /* GPIO number, NOT irq! */ + 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(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(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; +} diff --git a/arch/mips/alchemy/devboards/db1200.c b/arch/mips/alchemy/devboards/db1200.c new file mode 100644 index 000000000..da7663770 --- /dev/null +++ b/arch/mips/alchemy/devboards/db1200.c @@ -0,0 +1,969 @@ +/* + * 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/rawnand.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_to_nand(mtd); + 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 200ms timeout is + * because the carddetect usually triggers twice, after debounce. + */ +static irqreturn_t db1200_mmc_cd(int irq, void *ptr) +{ + disable_irq_nosync(irq); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t db1200_mmc_cdfn(int irq, void *ptr) +{ + void (*mmc_cd)(struct mmc_host *, unsigned long); + + /* link against CONFIG_MMC=m */ + mmc_cd = symbol_get(mmc_detect_change); + if (mmc_cd) { + mmc_cd(ptr, msecs_to_jiffies(200)); + symbol_put(mmc_detect_change); + } + + msleep(100); /* debounce */ + if (irq == DB1200_SD0_INSERT_INT) + enable_irq(DB1200_SD0_EJECT_INT); + else + enable_irq(DB1200_SD0_INSERT_INT); + + return IRQ_HANDLED; +} + +static int db1200_mmc_cd_setup(void *mmc_host, int en) +{ + int ret; + + if (en) { + ret = request_threaded_irq(DB1200_SD0_INSERT_INT, db1200_mmc_cd, + db1200_mmc_cdfn, 0, "sd_insert", mmc_host); + if (ret) + goto out; + + ret = request_threaded_irq(DB1200_SD0_EJECT_INT, db1200_mmc_cd, + db1200_mmc_cdfn, 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) +{ + disable_irq_nosync(irq); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t pb1200_mmc1_cdfn(int irq, void *ptr) +{ + void (*mmc_cd)(struct mmc_host *, unsigned long); + + /* link against CONFIG_MMC=m */ + mmc_cd = symbol_get(mmc_detect_change); + if (mmc_cd) { + mmc_cd(ptr, msecs_to_jiffies(200)); + symbol_put(mmc_detect_change); + } + + msleep(100); /* debounce */ + if (irq == PB1200_SD1_INSERT_INT) + enable_irq(PB1200_SD1_EJECT_INT); + else + enable_irq(PB1200_SD1_INSERT_INT); + + return IRQ_HANDLED; +} + +static int pb1200_mmc1_cd_setup(void *mmc_host, int en) +{ + int ret; + + if (en) { + ret = request_threaded_irq(PB1200_SD1_INSERT_INT, pb1200_mmc1_cd, + pb1200_mmc1_cdfn, 0, "sd1_insert", mmc_host); + if (ret) + goto out; + + ret = request_threaded_irq(PB1200_SD1_EJECT_INT, pb1200_mmc1_cd, + pb1200_mmc1_cdfn, 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; +} diff --git a/arch/mips/alchemy/devboards/db1300.c b/arch/mips/alchemy/devboards/db1300.c new file mode 100644 index 000000000..efb318e03 --- /dev/null +++ b/arch/mips/alchemy/devboards/db1300.c @@ -0,0 +1,872 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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/interrupt.h> +#include <linux/ata_platform.h> +#include <linux/mmc/host.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/rawnand.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/gpio-au1300.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_to_nand(mtd); + 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) +{ + disable_irq_nosync(irq); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t db1300_mmc_cdfn(int irq, void *ptr) +{ + void (*mmc_cd)(struct mmc_host *, unsigned long); + + /* 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(200)); + symbol_put(mmc_detect_change); + + msleep(100); /* debounce */ + if (irq == DB1300_SD1_INSERT_INT) + enable_irq(DB1300_SD1_EJECT_INT); + else + enable_irq(DB1300_SD1_INSERT_INT); + + 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_threaded_irq(DB1300_SD1_INSERT_INT, db1300_mmc_cd, + db1300_mmc_cdfn, 0, "sd_insert", mmc_host); + if (ret) + goto out; + + ret = request_threaded_irq(DB1300_SD1_EJECT_INT, db1300_mmc_cd, + db1300_mmc_cdfn, 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; +} diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c new file mode 100644 index 000000000..7d3dfaa10 --- /dev/null +++ b/arch/mips/alchemy/devboards/db1550.c @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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/rawnand.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/gpio-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_to_nand(mtd); + 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, 0, + /*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, 1, + /*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)); +} diff --git a/arch/mips/alchemy/devboards/db1xxx.c b/arch/mips/alchemy/devboards/db1xxx.c new file mode 100644 index 000000000..e6d25aad8 --- /dev/null +++ b/arch/mips/alchemy/devboards/db1xxx.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Alchemy DB/PB1xxx board support. + */ + +#include <asm/prom.h> +#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) +{ + mips_set_machine_name(board_type_str()); + 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); diff --git a/arch/mips/alchemy/devboards/platform.c b/arch/mips/alchemy/devboards/platform.c new file mode 100644 index 000000000..8d4b65c32 --- /dev/null +++ b/arch/mips/alchemy/devboards/platform.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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/setup.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(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 = kcalloc(cnt, sizeof(struct resource), 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 = kcalloc(5, sizeof(struct mtd_partition), 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; +} diff --git a/arch/mips/alchemy/devboards/platform.h b/arch/mips/alchemy/devboards/platform.h new file mode 100644 index 000000000..23d98fc09 --- /dev/null +++ b/arch/mips/alchemy/devboards/platform.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#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 diff --git a/arch/mips/alchemy/devboards/pm.c b/arch/mips/alchemy/devboards/pm.c new file mode 100644 index 000000000..73c778146 --- /dev/null +++ b/arch/mips/alchemy/devboards/pm.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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-au1000.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); |