/* 
 * plx.c
 *
 * Simple register read/write for the PLX PCI switches
 *
 */
#include <common.h>
#include <i2c.h>

#define PLX_I2C_CMD_REG_READ        0x04        /* I2C read command code */
#define PLX_I2C_CMD_REG_WRITE       0x03        /* I2C write command code */

uint plx_read_reg(uint addr, uint offset)
{
	unsigned char NtBit = 19;
	uint port = 0;
	uint Command;
	uint RegValue;
	uint NTValue = 0;

	/* Verify register offset */
	if (offset & 0x3) {
		printf("ERROR - Invalid register offset (0x%x)\n", offset);
		return 0;
	}

	/* Get offset within 4K port */
	offset = offset & 0xFFF;
	
	/* Convert offset to DWORD index */
	offset = offset >> 2;

	/* Build I2C command */
	Command =
		(PLX_I2C_CMD_REG_READ <<    24) |  /* Register operation   [26:24] */
		(NTValue              << NtBit) |  /* For NT-Link side port [20, 19, or 18] */
		(port                 <<    15) |  /* Dest port[18:15] OR Station[18:17] / port [16:15] */
		(0                    <<    14) |  /* Reserved [14] */
		(0xF                  <<    10) |  /* Byte enables [13:10] */
		(offset               <<     0);   /* Register DWORD address [9:0] */

	/* Read from the chip */
	if (i2c_read(addr, Command, 4, (u8*)&RegValue, 4) != 0)
		puts ("Error reading from the PLX.\n");

	return RegValue;
}

uint plx_write_reg(uint addr, uint offset, uint value)
{
	unsigned char NtBit = 19;
	uint port = 0;
	uint Command;
	uint NTValue = 0;

	/* Verify register offset */
	if (offset & 0x3) {
		printf("ERROR - Invalid register offset (0x%x)\n", offset);
		return 0;
	}

	/* Get offset within 4K port */
	offset = offset & 0xFFF;
	
	/* Convert offset to DWORD index */
	offset = offset >> 2;

	/* Build I2C command */
	Command =
		(PLX_I2C_CMD_REG_WRITE <<    24) |  /* Register operation   [26:24] */
		(NTValue               << NtBit) |  /* For NT-Link side port [20, 19, or 18] */
		(port                  <<    15) |  /* Dest port[18:15] OR Station[18:17] / port [16:15] */
		(0                     <<    14) |  /* Reserved [14] */
		(0xF                   <<    10) |  /* Byte enables [13:10] */
		(offset                <<     0);   /* Register DWORD address [9:0] */

	/* Read from the chip */
	if (i2c_write(addr, Command, 4, (u8*)&value, 4) != 0)
		puts ("Error writing to the PLX.\n");

	return 0;
}

int do_plx (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	int rcode = 0;
	uint addr, reg, value;

	switch (argc) {
		case 3: /* Get */
			addr = simple_strtoul(argv[1], NULL, 16);
			reg = simple_strtoul(argv[2], NULL, 16);
			value = plx_read_reg(addr,reg);
			printf ("%08x:%08x\n", reg, value);
			break;

		case 4: /* Set */
			addr = simple_strtoul(argv[1], NULL, 16);
			reg = simple_strtoul(argv[2], NULL, 16);
			value = simple_strtoul(argv[3], NULL, 16);
			plx_write_reg(addr,reg, value);
			break;

		default:
			printf("Usage: plx address register [value]\n");
			break;
		
	}

	return rcode;
}

U_BOOT_CMD(
	plx,	3,	2,	do_plx,
	"get/set plx register",
	"address register [value]\n"
);
