/*
 * diag_epi3_xem.c: Matthew Millman
 *
 * Routines for programming EPI3 personalities (specific to XEMs)
 */


/* 
 * Header files
 */

#include <common.h>
#include <config.h>
#include <command.h>
#include <epi3.h>
#include <diag.h>
#include <malloc.h>
#include <bootmenu.h>
#include <pci.h>
#include <i2c.h>
#include "diag_epi3_module.h"

#if defined(CONFIG_ATLAT10908) || defined(CONFIG_ATLAT9924TS)

/* 
 * File scope variables
 */

#ifdef CONFIG_ATLAT10908
static int xem_gpio_initialised = 0;
#endif

/*
 * Prototypes
 */

int diag_epi3_init_xem (int xem);
int diag_epi3_display_xem (int xem);
int diag_epi3_erase_xem_personality (int xem);
int diag_epi3_program_xem (int xem);
int diag_epi3_uninit_xems_at9924ts (void);
int diag_epi3_uninit_xems_at10908 (void);
int diag_epi3_uninit_xems (int);
int diag_epi3_read_xem_eeprom (int xem, epi3Personality * personality);
int diag_epi3_erase_xem_personality_area (int doit, int xem);

int
diag_epi3_display_xem (int xem)
{
    epi3Personality *personality;

    personality = malloc (sizeof (epi3Personality));


    if (diag_epi3_read_xem_eeprom (xem, personality))
    {
	diag_printf ("Error: Can't display personality\n");
	return 1;
    }

    if (epi3_dataok (personality))
    {
	diag_printf ("Error: No valid personality present\n");
	return 1;
    }

    diag_epi3_print_personality (personality);

    return 0;
}

int
diag_epi3_program_xem (int xem)
{
    char params[128];
    char buf[256];
    int key;

    epi3Personality *last_personality;

    last_personality = malloc (sizeof (epi3Personality));

    if (diag_epi3_read_xem_eeprom (xem, last_personality))
    {
	diag_printf ("Error: Can't read XEM EEPROM\n");
	goto fail;
    }

    if (epi3_dataok (last_personality))
    {
	free (last_personality);
	last_personality = NULL;;
    }

    if (diag_epi3_personality_menu (params, last_personality))
	goto fail;

    sprintf (buf, "epi3xmk.%u %s", xem, params);

    diag_printf
	("\n  Quick command to generate this personality in future (using CLI):\n\n  %s\n\n",
	 buf);

    diag_printf ("  Write this personality to XEM now? (Y/N) ==> ");

    key = serial_getc ();

    if (key != 'y' && key != 'Y')
	goto fail;

    diag_printf ("\n\n");

    run_command (buf, 0);
    if (last_personality)
	free (last_personality);
    return 0;

  fail:
    if (last_personality)
	free (last_personality);
    return 1;
}


int
cmd_get_xem_bay (char *arg, int default_bay)
{
    int len = strlen (arg);
    if (len > 7 && arg[len - 2] == '.')
    {
	switch (arg[len - 1])
	{
	case '1':
	    return 1;
	case '2':
	    return 2;
#ifdef CONFIG_ATLAT10908
	case '3':
	    return 3;
	case '4':
	    return 4;
	case '5':
	    return 5;
	case '6':
	    return 6;
	case '7':
	    return 7;
	case '8':
	    return 8;
#endif
	default:
	    return -1;
	}
    }
    return default_bay;
}

int
diag_epi3_init_xem_at9924ts (int xem, int reinit)
{
    int xemReady;
    int msCount;
    u8 byte_to_write;

    xemReady = 0;
    msCount = 0;

    if (reinit)
    {
	/* First power put everything back into the default state */

	*(volatile u8 *) 0xff000072 = 0x00;	/* Power down */
	*(volatile u8 *) 0xff000073 = 0x00;	/* Power down */

	*(volatile u8 *) 0xff000076 = 0x01;	/* Disable HS buffers */
	*(volatile u8 *) 0xff000077 = 0x01;	/* Disable HS buffers */

	*(volatile u8 *) 0xff000074 = 0x00;	/* Keep in reset */
	*(volatile u8 *) 0xff000075 = 0x00;	/* Keep in reset */
    }

    /*CR00020799 Delay between turn off then on of xem increased.
     *It was originally 50,000uS but this was not always enough
     *time for the 3v3 rail to turn off before being re-started
     *leaving it turned on some of the time.
     *appears to work on A60i at 300,000uS using 500,000uS
     */
    udelay (500000);		/* arbitrary delay */

    switch (xem)
    {
    case 1:
	byte_to_write = 0x04;
	i2c_write (0x70, 0x00, 0x01, &byte_to_write, 0x01);	/* Mux select. 
								 * This will fail on anything other than the SPSi
								 * ... because, the mux is only fitted on the SPSi
								 */

	*(volatile u8 *) 0xff000072 = 0x01;	/* Power up */
	do
	{
	    if ((*(volatile u8 *) 0xff000080 & 1) == 1)
	    {
		xemReady = 1;
		break;
	    }
	    udelay (10000);
	    msCount += 10;
	}
	while (msCount < 1000);

	if (!xemReady)
	{
	    diag_printf_always ("Error: XEM failed to power up\n");
	    goto fail;
	}
	else
	{
	    *(volatile u8 *) 0xff000076 = 0x00;	/* Enable HS buffers */
	}
	break;
    case 2:
	byte_to_write = 0x2;
	i2c_write (0x70, 0x00, 0x01, &byte_to_write, 0x01);	/* Mux select. 
								 * This will fail on anything other than the SPSi
								 * ... because, the mux is only fitted on the SPSi
								 */

	*(volatile u8 *) 0xff000073 = 0x01;	/* Power up */
	do
	{
	    if ((*(volatile u8 *) 0xff000081 & 1) == 1)
	    {
		xemReady = 1;
		break;
	    }

	    udelay (10000);
	    msCount += 10;
	}
	while (msCount < 1000);

	if (!xemReady)
	{
	    diag_printf_always ("Error: XEM failed to power up\n");
	    goto fail;
	}
	else
	{
	    *(volatile u8 *) 0xff000077 = 0x00;	/* Enable HS buffers */
	}
	break;
    default:
	goto fail;
    }

    udelay (50000);

    return 0;
  fail:
    return 1;


}

#ifdef CONFIG_ATLAT10908

int
diag_epi3_write_xem_pci_config (int xem, u16 config)
{
    switch (xem)
    {
    case 1:
	pci_write_config_word (PCI_BDF (1, 0x0C, 0), 0xC5,
			       SWAP_SHORT (config));
	break;
    case 2:
	pci_write_config_word (PCI_BDF (1, 0x0D, 0), 0xC5,
			       SWAP_SHORT (config));
	break;
    case 3:
	pci_write_config_word (PCI_BDF (1, 0x0E, 0), 0xC5,
			       SWAP_SHORT (config));
	break;
    case 4:
	pci_write_config_word (PCI_BDF (1, 0x0F, 0), 0xC5,
			       SWAP_SHORT (config));
	break;
    case 5:
	pci_write_config_word (PCI_BDF (0, 0x0C, 0), 0xC5,
			       SWAP_SHORT (config));
	break;
    case 6:
	pci_write_config_word (PCI_BDF (0, 0x0D, 0), 0xC5,
			       SWAP_SHORT (config));
	break;
    case 7:
	pci_write_config_word (PCI_BDF (0, 0x0E, 0), 0xC5,
			       SWAP_SHORT (config));
	break;
    case 8:
	pci_write_config_word (PCI_BDF (0, 0x0F, 0), 0xC5,
			       SWAP_SHORT (config));
	break;
    default:
	return 1;
	break;
    }

    return 0;
}

int
diag_epi3_read_xem_pci_config (int xem, u16 * config)
{
    switch (xem)
    {
    case 1:
	pci_read_config_word (PCI_BDF (1, 0x0C, 0), 0xC5, config);
	break;
    case 2:
	pci_read_config_word (PCI_BDF (1, 0x0D, 0), 0xC5, config);
	break;
    case 3:
	pci_read_config_word (PCI_BDF (1, 0x0E, 0), 0xC5, config);
	break;
    case 4:
	pci_read_config_word (PCI_BDF (1, 0x0F, 0), 0xC5, config);
	break;
    case 5:
	pci_read_config_word (PCI_BDF (0, 0x0C, 0), 0xC5, config);
	break;
    case 6:
	pci_read_config_word (PCI_BDF (0, 0x0D, 0), 0xC5, config);
	break;
    case 7:
	pci_read_config_word (PCI_BDF (0, 0x0E, 0), 0xC5, config);
	break;
    case 8:
	pci_read_config_word (PCI_BDF (0, 0x0F, 0), 0xC5, config);
	break;
    default:
	return 1;
	break;
    }

    *config = SWAP_SHORT (*config);

    return 0;
}

int
diag_epi3_init_pci_bridge_gpio (void)
{
    int i;

    *(volatile u8 *) 0xff000061 = 0xff;	/* All bridges out of reset */

    for (i = 0; i < 8; i++)
    {
	diag_epi3_write_xem_pci_config (i + 1, (u16) 0x22);
    }

    return 0;
}

int
diag_epi3_init_xem_at10908 (int xem, int reinit)
{
    int i;
    int xemReady;
    int msCount;

    xemReady = 0;
    msCount = 0;

    u16 xem_gpio_config[9];

    if (xem < 0 || xem > 8)
	return 1;

    for (i = 0; i < 8; i++)
    {
	xem_gpio_config[i + 1] = 0x22;
    }

    if (reinit)
    {
	*(volatile u8 *) 0xff0000d0 = 0x00;	/* reset bottom I2C */
	*(volatile u8 *) 0xff0000d1 = 0x00;	/* reset top I2C */

	*(volatile u8 *) 0xff000061 = 0xff;	/* All bridges out of reset */
    }

    if (xem >= 5 && xem <= 8)
    {
	u8 value = 0x00;
	*(volatile u8 *) 0xff0000d0 = 0x03;	/* bottom I2C out of reset */

	switch (xem)
	{
	case 5:
	    value = 0x01;
	    break;
	case 6:
	    value = 0x02;
	    break;
	case 7:
	    value = 0x04;
	    break;
	case 8:
	    value = 0x08;
	    break;
	default:
	    return 1;
	}

	i2c_write (0x70, 0x00, 0x01, &value, 0x01);	/* Mux select */

    }
    else if (xem >= 1 && xem <= 4)
    {
	u8 value = 0x00;
	*(volatile u8 *) 0xff0000d1 = 0x03;	/* bottom I2C out of reset */

	switch (xem)
	{
	case 1:
	    value = 0x01;
	    break;
	case 2:
	    value = 0x02;
	    break;
	case 3:
	    value = 0x04;
	    break;
	case 4:
	    value = 0x08;
	    break;
	default:
	    return 1;
	}

	i2c_write (0x71, 0x00, 0x01, &value, 0x01);	/* Mux select */
    }
    else
    {
	return 1;
    }

    if (!xem_gpio_initialised || reinit)
    {
	diag_epi3_init_pci_bridge_gpio ();
	xem_gpio_initialised = 1;
    }

    xem_gpio_config[xem] |= 0x40;

    if (diag_epi3_write_xem_pci_config (xem, xem_gpio_config[xem]))
	goto pcifail;

    //diag_printf_always("write xem %u %X\n", xem, xem_gpio_config[xem]);

    do
    {
	if (diag_epi3_read_xem_pci_config (xem, &xem_gpio_config[xem]))
	    goto pcifail;

	//diag_printf_always("0x%04X\n", xem_gpio_config[xem]);

	if (xem_gpio_config[xem] & 0x100)
	{
	    xemReady = 1;
	    break;
	}
	udelay (10000);
	msCount += 10;
    }
    while (msCount < 1000);

    if (!xemReady)
    {
	//diag_printf_always("timeout\n");
	goto pcifail;
    }

    xem_gpio_config[xem] |= 0x04;

    if (diag_epi3_write_xem_pci_config (xem, xem_gpio_config[xem]))
	goto pcifail;

    udelay (50000);

    return 0;

  pcifail:
    diag_printf_always ("Error: XEM PCI Config failed\n");
    return 1;

}

#endif /* CONFIG_ATLAT10908 */

int
diag_epi3_init_xem (int xem)
{
#ifdef CONFIG_ATLAT9924TS
    return diag_epi3_init_xem_at9924ts (xem, 1);
#endif /* CONFIG_ATLAT9924TS */
#ifdef CONFIG_ATLAT10908
    return diag_epi3_init_xem_at10908 (xem, 1);
#endif /* CONFIG_ATLAT10908 */
}

#ifdef CONFIG_ATLAT9924TS

int
diag_epi3_uninit_xems_at9924ts (void)
{
    *(volatile u8 *) 0xff000072 = 0x00;	/* Power down */
    *(volatile u8 *) 0xff000073 = 0x00;	/* Power down */

    *(volatile u8 *) 0xff000076 = 0x01;	/* Disable HS buffers */
    *(volatile u8 *) 0xff000077 = 0x01;	/* Disable HS buffers */

    *(volatile u8 *) 0xff000074 = 0x00;	/* Keep in reset */
    *(volatile u8 *) 0xff000075 = 0x00;	/* Keep in reset */

    return 0;
}

#endif /* CONFIG_ATLAT9924TS */

#ifdef CONFIG_ATLAT10908

int
diag_epi3_uninit_xems_at10908 (void)
{
    int i;

    *(volatile u8 *) 0xff0000d0 = 0x00;	/* reset bottom I2C */
    *(volatile u8 *) 0xff0000d1 = 0x00;	/* reset top I2C */

    for (i = 0; i < 8; i++)
    {
	if (diag_epi3_write_xem_pci_config (i + 1, 0x22))
	    return 1;
    }

    *(volatile u8 *) 0xff000061 = 0x00;	/* All bridges in reset */
    return 0;
}

#endif /* CONFIG_ATLAT10908 */

int
diag_epi3_uninit_xems (int dummy)
{
#ifdef CONFIG_ATLAT9924TS
    return diag_epi3_uninit_xems_at9924ts ();
#endif /* CONFIG_ATLAT9924TS */
#ifdef CONFIG_ATLAT10908
    return diag_epi3_uninit_xems_at10908 ();
#endif /* CONFIG_ATLAT10908 */

}

#ifdef CONFIG_ATLAT9924TS

int
diag_epi3_pwrdown_xem_at9924ts (int xem)
{
    if (xem == 1)
	*(volatile u8 *) 0xff000072 = 0x00;	/* Power down */
    else if (xem == 2)
	*(volatile u8 *) 0xff000073 = 0x00;	/* Power down */
    else
	return 1;

    if (xem == 1)
	*(volatile u8 *) 0xff000076 = 0x01;	/* Disable HS buffers */
    else if (xem == 2)
	*(volatile u8 *) 0xff000077 = 0x01;	/* Disable HS buffers */
    else
	return 1;

    return 0;
}

#endif /* CONFIG_ATLAT9924TS */

#ifdef CONFIG_ATLAT10908

int
diag_epi3_pwrdown_xem_at10908 (int xem)
{
    return diag_epi3_write_xem_pci_config (xem, 0x22);
}

#endif /* CONFIG_ATLAT10908 */

int
diag_epi3_pwrdown_xem (int xem)
{
#ifdef CONFIG_ATLAT9924TS
    return diag_epi3_pwrdown_xem_at9924ts (xem);
#endif /* CONFIG_ATLAT9924TS */
#ifdef CONFIG_ATLAT10908
    return diag_epi3_pwrdown_xem_at10908 (xem);
#endif /* CONFIG_ATLAT10908 */
}

int
diag_epi3_read_xem_eeprom (int xem, epi3Personality * personality)
{
    u8 addr;
    int offset = 0;

#if defined(CONFIG_ATLAT10908)
    addr = 0x52;
#elif defined (CONFIG_ATLAT9924TS)
    if (xem == 1)
	addr = 0x52;
    else
	addr = 0x53;
#else
#error No I2C address for EEPROM
#endif

    if (diag_epi3_init_xem (xem))
	goto fail;

    do
    {
	int i = 0;
	while (1)
	{

	    if (i++ >= 100)
	    {
		diag_printf_always
		    ("Error: Exceeded maximum number of retries (Read)\n");
		goto fail;
	    }

	    if (!i2c_read
		(addr, offset, 1, (u8 *) ((u32) personality + offset), 0x01))
	    {
		offset++;
		break;
	    }
	    else
	    {
		diag_printf_always
		    ("Retry read: addr=0x%02X offset=0x%02X dest=0x%08X offset(d)=%u\n",
		     addr, offset, ((u32) personality + offset), offset);
		udelay (3000);
		continue;
	    }
	}
    }
    while (offset < 0x100);

    diag_epi3_uninit_xems (0);

    return 0;

  fail:
    diag_epi3_uninit_xems (0);
    return 1;
}

int
diag_epi3_erase_xem_personality (int xem)
{
    u8 blank = 0xff;
    int offset;
    u8 addr;

#if defined(CONFIG_ATLAT10908)
    addr = 0x52;
#elif defined (CONFIG_ATLAT9924TS)
    if (xem == 1)
	addr = 0x52;
    else
	addr = 0x53;
#else
#error No I2C address for EEPROM
#endif
    offset = 0;

    if (diag_epi3_init_xem (xem))
	goto fail;

    do
    {
	int i = 0;
	while (1)
	{

	    if (i++ >= 100)
	    {
		diag_printf_always
		    ("Error: Exceeded maximum number of retries (Erase)\n");
		goto fail;
	    }

	    if (!i2c_write (addr, offset, 1, &blank, 0x01))
	    {
		udelay (3000);
		offset++;
		break;
	    }
	    else
	    {
		//diag_printf_always("Retry write: offset=0x%02X src=0x%08X offset(d)=%u data=0x%02X\n", 
		//              offset, &blank, offset, blank);
		udelay (3000);
		continue;
	    }
	}
    }
    while (offset < 0x100);

    diag_epi3_uninit_xems (0);
    return 0;

  fail:
    diag_epi3_uninit_xems (0);
    return 1;
}

int
diag_epi3_write_xem_personality (int xem, epi3Personality * personality)
{
    int offset;
    u8 addr;
    epi3Personality personality_readback;

#if defined(CONFIG_ATLAT10908)
    addr = 0x52;
#elif defined (CONFIG_ATLAT9924TS)
    if (xem == 1)
	addr = 0x52;
    else
	addr = 0x53;
#else
#error No I2C address for EEPROM
#endif
    offset = 0;

    if (diag_epi3_init_xem (xem))
	return 1;

    do
    {
	int i = 0;
	while (1)
	{
	    if (i++ >= 100)
	    {
		diag_printf_always
		    ("Error: Exceeded maximum number of retries (Write)\n");
		goto fail;
	    }
	    if (!i2c_write
		(addr, offset, 1, (void *) ((u32) personality + offset),
		 0x01))
	    {
		udelay (3000);
		offset++;
		break;
	    }
	    else
	    {
		//diag_printf_always("Retry write: offset=0x%02X src=0x%08X offset(d)=%u data=0x%02X\n", 
		//              offset, ((u32)personality + offset), offset, *(volatile u8 *)((u32)personality + offset));
		udelay (3000);
		continue;
	    }
	}

    }
    while (offset < 0x100);

    /* Now read back */

    offset = 0;

    do
    {
	int i = 0;
	while (1)
	{

	    if (i++ >= 100)
	    {
		diag_printf_always
		    ("Error: Exceeded maximum number of retries (Read Back)\n");
		goto fail;
	    }

	    if (!i2c_read
		(addr, offset, 1,
		 (u8 *) ((u32) & personality_readback + offset), 0x01))
	    {
		offset++;
		break;
	    }
	    else
	    {
		//diag_printf_always("Retry read: offset=0x%02X dest=0x%08X offset(d)=%u\n", offset, 
		//              ((u32)personality + offset), offset);
		udelay (3000);
		continue;
	    }

	}
    }
    while (offset < 0x100);

    if (memcmp (&personality_readback, personality, sizeof (epi3Personality)))
    {
	diag_printf_always
	    ("Error: Read and Written personalities do not match\n");
	goto fail;
    }

    diag_epi3_uninit_xems (0);
    return 0;

  fail:
    diag_epi3_uninit_xems (0);
    return 1;;
}

/**
 * Routine for powering up/down XEM bays
 *
 * @param cmdtp command internal data
 * @param flag command flag
 * @param argc number of arguments supplied to the command
 * @param argv arguments list
 * @return 0 on success, 1 otherwise
 */

int
do_xempwr (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
    int bay;

    bay = cmd_get_xem_bay (argv[0], 0);

    if (!bootmenu_stricmp (argv[1], "on"))
    {
	if (!bay)
	{
	    fprintf (stderr, "Error: Invalid XEM bay specified\n");
	    return 1;
	}
#ifdef CONFIG_ATLAT9924TS
	return diag_epi3_init_xem_at9924ts (bay, 0);
#endif /* CONFIG_ATLAT9924TS */
#ifdef CONFIG_ATLAT10908
	return diag_epi3_init_xem_at10908 (bay, 0);
#endif /* CONFIG_ATLAT10908 */
    }
    else if (!bootmenu_stricmp (argv[1], "off"))
    {
	if (!bay)
	{
	    fprintf (stderr, "Error: Invalid XEM bay specified\n");
	    return 1;
	}
	return diag_epi3_pwrdown_xem (bay);
    }
    else if (!bootmenu_stricmp (argv[1], "reset"))
    {
	return diag_epi3_uninit_xems (0);
    }
    else
    {
	fprintf (stderr, "Error: Invalid parameter\n");
    }

    return 0;
}

/**
 * Routine for writing an EPI3 block to a XEM
 * Part of the AlliedWare+ release system
 * argv[1]:Serial argv[2]:Manufacturer argv[3]:Year argv[4]:Month argv[5]:BoardID
 * argv[6]:PCBRev argv[7]:MAC argv[8]:844Bom argv[9]:820Bom argv[10]:OEM
 * argv[11]:HWModLevel argv[12-]:ChipRevisions
 *
 * @param cmdtp command internal data
 * @param flag command flag
 * @param argc number of arguments supplied to the command
 * @param argv arguments list
 * @return 0 on success, 1 otherwise
 */

int
do_epi3_epi3xmk (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
    int bay;
    epi3Personality *new_personality;
    epi3Personality *last_personality;


    if ((bay = cmd_get_xem_bay (argv[0], -1)) < 1)
    {
	diag_printf_always
	    ("Error: No/Invalid bay specified. Use epi3xmk.N where N is bay to program.\n");
	return 1;
    }

    last_personality = malloc (sizeof (epi3Personality));
    new_personality = malloc (sizeof (epi3Personality));

    if (diag_epi3_read_xem_eeprom (bay, last_personality))
    {
	diag_printf ("Error: Failed to read existing personality\n");
	goto fail;
    }



    if (epi3_dataok (last_personality))
    {
	free (last_personality);
	last_personality = NULL;
    }

    //diag_printf_always("last_personality 0x%08X\n", last_personality);

    memset (new_personality, 0, sizeof (epi3Personality));

    new_personality->epiVersion = 0x01;
    new_personality->epiMagicNumber = 0x55;

    if (argc < 2)
    {
	diag_printf_always
	    ("Error: Too few parameters. Type 'help epi3xmk' for more information.\n");
	goto fail;
    }

    if (diag_epi3mk_backend (argc, argv, last_personality, new_personality))
	goto fail;

    if (diag_epi3_write_xem_personality (bay, new_personality))
    {
	diag_printf_always ("Error: Write failed\n");
	goto fail;
    }

    diag_printf_always ("Successfully wrote EPI3 information to XEM: %u\n",
			bay);

    if (last_personality)
	free (last_personality);
    free (new_personality);

    return 0;

  fail:

    diag_printf_always ("Error: Failed to write XEM personality\n");

    if (last_personality)
	free (last_personality);
    free (new_personality);

    return 1;
}

int
diag_epi3_erase_xem_personality_area (int doit, int xem)
{
    char key;

    key = 0;

    if (!doit)
    {
	diag_printf
	    ("\n  WARNING: This option will destroy the contents of the XEM personality EEPROM."
	     "\n  The personality should not be erased under any normal circumstance."
	     "\n  Press Y to proceed, or any other key to return to the previous menu ==> ");

	key = serial_getc ();

	if ((key >= 0x20) && (key <= 0x7E))
	{
	    serial_putc (key);
	}

    }

    diag_printf ("\n");

    if (key == 'y' || key == 'Y' || doit)
    {
	if (diag_epi3_erase_xem_personality (xem))
	{
	    diag_printf ("Error: Failed to erase EEPROM\n");
	    return 1;
	}

    }

    return 0;
}

static struct epi3_mod_ops xem_funcs = {
	.init_comms = diag_epi3_init_xem,
	.deinit_comms = diag_epi3_uninit_xems,
	.display = diag_epi3_display_xem,
	.program = diag_epi3_program_xem,
	.erase = diag_epi3_erase_xem_personality_area,
};

int
diag_epi3_xem_init(void)
{
	return diag_epi3_module_register (EPI3_TYPE_XEM, &xem_funcs);
}

U_BOOT_CMD (epi3xmk, 11, 2, do_epi3_epi3xmk,
	    "epi3xmk\t- Make an EPI3 block in a XEM\n",
	    "[.N] [Serial] [Mfg] [Year] [Month] [BoardID] [PCBRev] [MAC] [844Bom] [OEM] [HWModLevel]\n\n"
	    "Standard command usage:\n\n"
	    "\tepi3xmk.1 123456789 A 06 06 270 X2 001122334455 A U 1\n\n"
	    "The 'Serial' parameter is required but ignored. The only case where this parameter is observed is when the\n"
	    "personality area is blank (factory first program).\n\n"
	    "If a parameter is specified as a dash '-' character, then the value is copied from the last valid personality.\n"
	    "There is no need to insert the '-' character in place of values after any of those you wish to modify.\n"
	    "For example, the command:\n\n"
	    "\tepi3xmk - - - - 270\n\n"
	    "Will make a new personality, with all attributes unchanged apart from the specified BoardID.\n");

U_BOOT_CMD (xempwr, 2, 2, do_xempwr,
	    "xempwr\t- Power control in XEM bays\n",
	    ".N [on|off|reset|init]\n" "where N is a number between 1 and "
#if defined(CONFIG_ATLAT9924TS)
	    "2"
#elif defined(CONFIG_ATLAT10908)
	    "8"
#else
#error No valid product
#endif
	    "\n"
	    "'reset' is used to return all power and i2c mux's to default state (N=ignored)\n");

#endif /* defined(CONFIG_ATLAT10908) || defined(CONFIG_ATLAT9924TS) */
