#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/mm.h>
#include <linux/timedoctor.h>
#include <asm/bootinfo.h>

#include <prjconfig.h>
#define ENABLE_PNX833X_GPIO_REGS_DIRECT_ACCESS
#include <pnx833x.h>
#include <gpio.h>
#include <linux/proc_fs.h>
#include <asm/mach-pnx833x/brightsign_id.h>

/* endianess twiddlers */
#define PNX8335_DEBUG0 0x5400
#define PNX8335_DEBUG1 0x5404
#define PNX8335_DEBUG2 0x5408
#define PNX8335_DEBUG3 0x540c
#define PNX8335_DEBUG4 0x5410
#define PNX8335_DEBUG5 0x5414
#define PNX8335_DEBUG6 0x5418
#define PNX8335_DEBUG7 0x541c

#define PNX8335_DEBUG2_SATA_DMA_DESC_ENDIAN	(1 << 11)
#define PNX8335_DEBUG2_SATA_DMA_READ_ENDIAN	(1 << 7)
#define PNX8335_DEBUG5_SATA_DMA_WRITE_ENDIAN	(1 << 30)

int prom_argc = 0;
char **prom_argv = 0, **prom_envp = 0;

u32 brightsign_board_id;

extern void prom_init_cmdline(void);
extern char *prom_getenv(char *envname);

const char *get_system_type(void)
{
	return "BrightSign";
}

// We must read this at boot time before the GPIOs start being used
// for their other purposes.
static void identify_board(void)
{
	const char *board_name;
	
#if defined(CONFIG_INPUT_BRIGHTSIGN_MOJITO_IO)
	// On Mojito only GPIO6 and GPIO7 contribute to the board ID
	const u32 ID_SHIFT = 6;
	const u32 ID_PINS = 0x3 << ID_SHIFT;
	const u32 ID_GROUP = 0x1000;
#else
	const u32 ID_SHIFT = 6;
	const u32 ID_PINS = 0xf << ID_SHIFT;
	const u32 ID_GROUP = 0;
#endif
	
	// Configure the board ID pins as inputs.
	PNX833X_PIO_DIR2 &= ~ID_PINS;
	brightsign_board_id = (PNX833X_PIO_IN & ID_PINS) >> ID_SHIFT;
	brightsign_board_id |= ID_GROUP;

	switch (brightsign_board_id)
	{
	case BS_BOARD_MONACO_A:
		board_name = "Monaco REVA/B";
		break;
	case BS_BOARD_MONACO_C:
		board_name = "Monaco REVC/D";
		break;
	case BS_BOARD_MONACO_E:
		board_name = "Monaco REVE/F/G";
		break;		
	case BS_BOARD_PANDORA3_A:
		board_name = "Pandora3";
		break;
	case BS_BOARD_APOLLO_A:
		board_name = "Apollo REVA";
		pr_err("\nThis hardware is not supported by this build.\n\n");
		break;
	case BS_BOARD_APOLLO_C:
		board_name = "Apollo REVC/D";
		break;
	default:
		board_name = "Unknown board hardware revision";
		break;
	};
	
	printk("Board ID: 0x%x %s\n", brightsign_board_id, board_name);
}

static inline unsigned long env_or_default(char *env, unsigned long dfl)
{
	char *str = prom_getenv(env);
	return str ? simple_strtol(str, 0, 0) : dfl;
}

void __init prom_init(void)
{
	unsigned long memsize;
	unsigned long memstart;

	prom_argc = fw_arg0;
	prom_argv = (char **)fw_arg1;
	prom_envp = (char **)fw_arg2;

	prom_init_cmdline();

	/* We are using for the MAIN MIPS
	   board memory size - CONFIG_DSP_MIPS_MEM_SIZE - CONFIG_RESERVED_HIGH_MEM_SIZE
	   and we 'assume' that the DSP MIPS memory starts at
	   256MB - CONFIG_DSP_MIPS_MEM_SIZE - CONFIG_RESERVED_HIGH_MEM_SIZE.
	   This would make use of the 'feature' that access to non-existant
	   memory 'wraps around' to lower 128MB / 64MB memory banks.
	*/

	/* Proper memory size (e.g. 64mb for stb220t3) should be passed
	   by bootloader */
	memsize = env_or_default("memsize", 0x55);
	if (memsize == 0x55) 
	{
		// Monaco has 256Mbytes of available SDRAM
		memsize = 256 * 1024 * 1024;
	}

#ifdef CONFIG_SOC_PNX8335
#ifdef CONFIG_PNX8335_AUDIO_MIPS
        /* Memory start is calculated from load address - standard 0x1000 */
        memstart = ((unsigned long)CONFIG_DSP_MIPS_LOAD_ADDR) - CAC_BASE - 0x1000;
        memsize = (unsigned long)CONFIG_DSP_MIPS_MEM_SIZE;
#else
        /* Main CPU always runs from 0MB upwards. */
        memstart = 0UL;
        /* Upperlimit is by autosensing memsize - dsp memsize - reserved memsize. */
        memsize -= (CONFIG_DSP_MIPS_MEM_SIZE + CONFIG_RESERVED_HIGH_MEM_SIZE);
#endif
#else
        /* Main CPU always runs from 0MB upwards. */
        memstart = 0UL;
        /* Upperlimit is by autosensing memsize - reserved memsize. */
        memsize -= CONFIG_RESERVED_HIGH_MEM_SIZE;
#endif
	add_memory_region(memstart, memsize, BOOT_MEM_RAM);
}

void __init pnx833x_board_setup(void)
{
	identify_board();

	/*
	 * Note: MIU setup for NAND or NOR is assumed to have already have
	 * been completed by the bootloader (exact setup may be platform
	 * specific and we want to avoid platform specific code in the
	 * kernel if possible). Flash related GPIOs such as NAND RDY and WP
	 * are also left to the bootloader, for the same reason.
	 */

#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)
	// On Monaco GPIO25 is used to enable the hub chip or our USB
	// device via VBUS_DET. GPIO26 does nothing.
#if defined (CONFIG_KERNEL_GPIO_INIT_USB)
	gpio_select_output(25);
	gpio_write(1, 25);
#endif
#endif

#if defined(CONFIG_IP3902) || defined(CONFIG_IP3902_MODULE)
	/* Configure GPIOs 28-33 for MII interface to Ethernet phy */
	gpio_select_function_alt(27);
	gpio_select_function_alt(28);
#if defined(CONFIG_NETPHY_RESET_PIO_29)
	/* Do Nothing, let gpio function as pio */
#else
	gpio_select_function_alt(29);
#endif
	gpio_select_function_alt(30);
	gpio_select_function_alt(31);
	gpio_select_function_alt(32);
	gpio_select_function_alt(33);

#if defined (CONFIG_MOCA_PHY)
	gpio_select_function_io(13);
	gpio_select_output(13);
	gpio_select_function_io(14);
	gpio_select_output(14);
	gpio_write(0, 13);
	gpio_write(0, 14);
#endif
#endif

#if defined(CONFIG_SATA_PNX833X) || defined(CONFIG_SATA_PNX833X_MODULE)
	{
		u32 val;
		val = PNX833X_REG(PNX8335_DEBUG2);
#ifdef __LITTLE_ENDIAN
		val |= PNX8335_DEBUG2_SATA_DMA_DESC_ENDIAN;
#else
		val &= ~PNX8335_DEBUG2_SATA_DMA_DESC_ENDIAN;
#endif
		val |= PNX8335_DEBUG2_SATA_DMA_READ_ENDIAN;
		PNX833X_REG(PNX8335_DEBUG2) = val;
		val = PNX833X_REG(PNX8335_DEBUG5);
		val |= PNX8335_DEBUG5_SATA_DMA_WRITE_ENDIAN;
		PNX833X_REG(PNX8335_DEBUG5) = val;
	}
#endif


#ifndef CONFIG_PNX8335_AUDIO_MIPS
	printk(KERN_INFO "PNX833x DDR PLL configuration: M=0x%08lx, NP=0x%08lx\n",
	       PNX8335_CLOCK_PLL_DDR_M_CTL,
	       PNX8335_CLOCK_PLL_DDR_NP_CTL);
#endif
}


static int boardid_procmem(char *page, char **start, off_t offset, int count, int *eof, void *data)
{
	const char *family;
	char rev;

	// Unfortunately the board ID flags are not unique for Mojito. :(
	switch (brightsign_board_id)
	{
        case BS_BOARD_MOJITO_A:
		family = "mojito";
		rev = 'A';
                break;
        case BS_BOARD_MONACO_A:
		family = "monaco";
		rev = 'A';
                break;
        case BS_BOARD_MONACO_C:
		family = "monaco";
		rev = 'C';
                break;
        case BS_BOARD_MONACO_E:
		family = "monaco";
		rev = 'E';
                break;          
        case BS_BOARD_PANDORA3_A:
		family = "pandora3";
		rev = 'A';
                break;
	case BS_BOARD_APOLLO_A:
		family = "apollo";
		rev = 'A';
		break;
	case BS_BOARD_APOLLO_C:
		family = "apollo";
		rev = 'C';
		break;
        default:
                pr_err("\nUnknown board hardware revision 0x%x\n\n", brightsign_board_id);
		family = "Unknown";
		rev = '?';
                break;
        };

	*eof = 1;
	return snprintf(page, PAGE_SIZE, "Family: %s\nRevision: %c\nID: 0x%x\n", family, rev, brightsign_board_id);
}

static int __init boardid_init(void)
{
	if (!create_proc_read_entry("boardid", 0, NULL, boardid_procmem, NULL))
		pr_err("Failed to create /proc/boardid\n");
	return 0;
}

static void __exit boardid_exit(void)
{
	remove_proc_entry("boardid", NULL);
}

module_init(boardid_init);
module_exit(boardid_exit);
