/*
 *  Support for the Arcom ZEUS.
 *
 *  Copyright (C) 2006 Arcom Control Systems Ltd.
 *
 *  Loosely based on Arcom's 2.6.16.28.
 *  Maintained by Marc Zyngier <maz@misterjones.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 */

#include <linux/cpufreq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/pm.h>
#include <linux/gpio.h>
#include <linux/serial_8250.h>
#include <linux/dm9000.h>
#include <linux/mmc/host.h>
#include <linux/spi/spi.h>
#include <linux/spi/pxa2xx_spi.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <linux/i2c.h>
#include <linux/i2c/pxa-i2c.h>
#include <linux/i2c/pca953x.h>
#include <linux/apm-emulation.h>
#include <linux/can/platform/mcp251x.h>

#include <asm/mach-types.h>
#include <asm/suspend.h>
#include <asm/system_info.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>

#include <mach/pxa27x.h>
#include <mach/regs-uart.h>
#include <linux/platform_data/usb-ohci-pxa27x.h>
#include <linux/platform_data/mmc-pxamci.h>
#include <mach/pxa27x-udc.h>
#include <mach/udc.h>
#include <linux/platform_data/video-pxafb.h>
#include <mach/pm.h>
#include <mach/audio.h>
#include <linux/platform_data/pcmcia-pxa2xx_viper.h>
#include <mach/zeus.h>
#include <mach/smemc.h>

#include "generic.h"

/*
 * Interrupt handling
 */

static unsigned long zeus_irq_enabled_mask;
static const int zeus_isa_irqs[] = { 3, 4, 5, 6, 7, 10, 11, 12, };
static const int zeus_isa_irq_map[] = {
	0,		/* ISA irq #0, invalid */
	0,		/* ISA irq #1, invalid */
	0,		/* ISA irq #2, invalid */
	1 << 0,		/* ISA irq #3 */
	1 << 1,		/* ISA irq #4 */
	1 << 2,		/* ISA irq #5 */
	1 << 3,		/* ISA irq #6 */
	1 << 4,		/* ISA irq #7 */
	0,		/* ISA irq #8, invalid */
	0,		/* ISA irq #9, invalid */
	1 << 5,		/* ISA irq #10 */
	1 << 6,		/* ISA irq #11 */
	1 << 7,		/* ISA irq #12 */
};

static inline int zeus_irq_to_bitmask(unsigned int irq)
{
	return zeus_isa_irq_map[irq - PXA_ISA_IRQ(0)];
}

static inline int zeus_bit_to_irq(int bit)
{
	return zeus_isa_irqs[bit] + PXA_ISA_IRQ(0);
}

static void zeus_ack_irq(struct irq_data *d)
{
	__raw_writew(zeus_irq_to_bitmask(d->irq), ZEUS_CPLD_ISA_IRQ);
}

static void zeus_mask_irq(struct irq_data *d)
{
	zeus_irq_enabled_mask &= ~(zeus_irq_to_bitmask(d->irq));
}

static void zeus_unmask_irq(struct irq_data *d)
{
	zeus_irq_enabled_mask |= zeus_irq_to_bitmask(d->irq);
}

static inline unsigned long zeus_irq_pending(void)
{
	return __raw_readw(ZEUS_CPLD_ISA_IRQ) & zeus_irq_enabled_mask;
}

static void zeus_irq_handler(unsigned int irq, struct irq_desc *desc)
{
	unsigned long pending;

	pending = zeus_irq_pending();
	do {
		/* we're in a chained irq handler,
		 * so ack the interrupt by hand */
		desc->irq_data.chip->irq_ack(&desc->irq_data);

		if (likely(pending)) {
			irq = zeus_bit_to_irq(__ffs(pending));
			generic_handle_irq(irq);
		}
		pending = zeus_irq_pending();
	} while (pending);
}

static struct irq_chip zeus_irq_chip = {
	.name		= "ISA",
	.irq_ack	= zeus_ack_irq,
	.irq_mask	= zeus_mask_irq,
	.irq_unmask	= zeus_unmask_irq,
};

static void __init zeus_init_irq(void)
{
	int level;
	int isa_irq;

	pxa27x_init_irq();

	/* Peripheral IRQs. It would be nice to move those inside driver
	   configuration, but it is not supported at the moment. */
	irq_set_irq_type(gpio_to_irq(ZEUS_AC97_GPIO), IRQ_TYPE_EDGE_RISING);
	irq_set_irq_type(gpio_to_irq(ZEUS_WAKEUP_GPIO), IRQ_TYPE_EDGE_RISING);
	irq_set_irq_type(gpio_to_irq(ZEUS_PTT_GPIO), IRQ_TYPE_EDGE_RISING);
	irq_set_irq_type(gpio_to_irq(ZEUS_EXTGPIO_GPIO),
			 IRQ_TYPE_EDGE_FALLING);
	irq_set_irq_type(gpio_to_irq(ZEUS_CAN_GPIO), IRQ_TYPE_EDGE_FALLING);

	/* Setup ISA IRQs */
	for (level = 0; level < ARRAY_SIZE(zeus_isa_irqs); level++) {
		isa_irq = zeus_bit_to_irq(level);
		irq_set_chip_and_handler(isa_irq, &zeus_irq_chip,
					 handle_edge_irq);
		set_irq_flags(isa_irq, IRQF_VALID | IRQF_PROBE);
	}

	irq_set_irq_type(gpio_to_irq(ZEUS_ISA_GPIO), IRQ_TYPE_EDGE_RISING);
	irq_set_chained_handler(gpio_to_irq(ZEUS_ISA_GPIO), zeus_irq_handler);
}


/*
 * Platform devices
 */

/* Flash */
static struct resource zeus_mtd_resources[] = {
	[0] = { /* NOR Flash (up to 64MB) */
		.start	= ZEUS_FLASH_PHYS,
		.end	= ZEUS_FLASH_PHYS + SZ_64M - 1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = { /* SRAM */
		.start	= ZEUS_SRAM_PHYS,
		.end	= ZEUS_SRAM_PHYS + SZ_512K - 1,
		.flags	= IORESOURCE_MEM,
	},
};

static struct physmap_flash_data zeus_flash_data[] = {
	[0] = {
		.width		= 2,
		.parts		= NULL,
		.nr_parts	= 0,
	},
};

static struct platform_device zeus_mtd_devices[] = {
	[0] = {
		.name		= "physmap-flash",
		.id		= 0,
		.dev		= {
			.platform_data = &zeus_flash_data[0],
		},
		.resource	= &zeus_mtd_resources[0],
		.num_resources	= 1,
	},
};

/* Serial */
static struct resource zeus_serial_resources[] = {
	{
		.start	= 0x10000000,
		.end	= 0x1000000f,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= 0x10800000,
		.end	= 0x1080000f,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= 0x11000000,
		.end	= 0x1100000f,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= 0x40100000,
		.end	= 0x4010001f,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= 0x40200000,
		.end	= 0x4020001f,
		.flags	= IORESOURCE_MEM,
