/*====================================================================*
 *
 *   Copyright (c) 2013 Qualcomm Atheros, Inc.
 *
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or
 *   without modification, are permitted (subject to the limitations
 *   in the disclaimer below) provided that the following conditions
 *   are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *   * Neither the name of Qualcomm Atheros nor the names of
 *     its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written
 *     permission.
 *
 *   NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
 *   GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
 *   COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 *   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 *   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 *   OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *--------------------------------------------------------------------*/

#ifndef CHIPSET_SOURCE
#define CHIPSET_SOURCE

/*====================================================================*
 *   custom header files;
 *--------------------------------------------------------------------*/

#include "../plc/plc.h"
#include "../tools/types.h"
#include "../tools/symbol.h"

/*====================================================================*
 *
 *   char const * chipsetname (uint8_t MDEVICE)
 *
 *   plc.h
 *
 *   return the ASCII name string associated with the MDEVICE_CLASS
 *   field in the VS_SW_VER.CNF message; this field represents the
 *   chipset family or class of device;
 *
 *   the MDEVICE_CLASS field was named MDEVICEID at one time;
 *
 *   Contributor(s):
 *      Charles Maier
 *
 *--------------------------------------------------------------------*/

char const * chipsetname (uint8_t MDEVICE_CLASS)

{
	static const struct _type_ chipname [] =
	{
		{
			CHIPSET_UNKNOWN,
			"UNKNOWN"
		},
		{
			CHIPSET_INT6000A1,
			"INT6000"
		},
		{
			CHIPSET_INT6300A0,
			"INT6300"
		},
		{
			CHIPSET_INT6400A0,
			"INT6400"
		},
		{
			CHIPSET_AR7400A0,
			" AR7400"
		},
		{
			CHIPSET_AR6405A0,
			" AR6405"
		},
		{
			CHIPSET_PANTHER_LYNX,
			"PANTHER/LYNX"
		},
		{
			CHIPSET_QCA7450A0,
			"QCA7450"
		},
		{
			CHIPSET_QCA7451A0,
			"QCA7451"
		},
		{
			CHIPSET_QCA7420A0,
			"QCA7420"
		},
		{
			CHIPSET_QCA6410A0,
			"QCA6410"
		},
		{
			CHIPSET_QCA7006AQA0,
			"QCA7006AQ"
		},
		{
			CHIPSET_QCA7000A0,
			"QCA7000"
		},
		{
			CHIPSET_QCA7005A0,
			"QCA7005"
		},
		{
			CHIPSET_QCA7500A0,
			"QCA7500"
		}
	};
	return (typename (chipname, SIZEOF (chipname), MDEVICE_CLASS, chipname [0].name));
}

/*====================================================================*
 *
 *   char const * chipsetname_by_ident (uint32_t ident, uint8_t chipset)
 *
 *   chipset.h
 *
 *   return the ASCII name string associated with the IDENT (aka STRAP)
 *   field in the VS_SW_VER.CNF message;
 *   in case of unknown value, fallback using chipsetname function
 *
 *--------------------------------------------------------------------*/

char const * chipsetname_by_ident (uint32_t ident, uint8_t chipset)
{
	static const struct _type_ chipname [] =
	{
		{ 0x00000042, "INT6000" },
		{ 0x00006300, "INT6300" },
#if 0
		/* don't list these two here since we would need MDEVICE_CLASS
		 * (aka our chipset parameter) to differentiate; instead we rely
		 *  on older chipsetname function to handle it during fallback...
		 */
		{ 0x00006400, " AR6405" },
		{ 0x00006400, "INT6400" },
#endif
		{ 0x00007400, " AR7400" },
		{ 0x001B587C, "QCA7005" },
		{ 0x001B589C, "QCA7000" },
		{ 0x001B58AC, "QCA7006AQ" },
		{ 0x001B58BC, "QCA6411" },
		{ 0x001B58DC, "QCA7000" },
		{ 0x001B58EC, "QCA6410" },
		{ 0x001CFC00, "QCA7420" },
		{ 0x001CFCFC, "QCA7420" },
		{ 0x001D4C00, "QCA7500" },
		{ 0x001D4C0F, "QCA7500" },
		{ 0x0E001D1A, "QCA7451" },
		{ 0x0F001D1A, "QCA7450" },
	};
	return (typename (chipname, SIZEOF (chipname), ident, chipsetname (chipset)));
}

/*====================================================================*
 *
 *   void chipset (void const * memory, uint32_t * ident);
 *
 *   chipset.h
 *
 *   replace VS_SW_VER message MDEVICE_CLASS field with correct value;
 *   the MDEVICE_CLASS field was named MDEVICEID at one time;
 *   while at save the ident field to given (since the offset varies,
 *   it can be used by caller later more easily);
 *
 *   Atheros chipsets are identified by code in the VS_SW_VER vendor
 *   specific management message; the chipset [] vector translates a
 *   chipset code to a chipset name;
 *
 *   the basic assumption is that the firmware always tells the truth
 *   but the bootrom does not; because of engineering changes, the
 *   firmware uses a different device identification scheme than that
 *   used by the bootrom and that information appears in different
 *   locations depending on the source of the VS_SW_VER confirmation;
 *   see the Programmer's Guide for more information.
 *
 *   INT6000   0x01 / 0x01  0x00000042 / NA
 *   INT6300   0x01 / 0x02  0x00006300 / NA
 *   INT6400   0x03 / 0x03  0x00006400 / NA
 *    AR7400   0x03 / 0x04  0x00007400 / NA
 *    AR6405   0x03 / 0x05  0x00006400 / NA
 *   QCA7450   0x03 / 0x20  0x0F001D1A / NA
 *   QCA7420   0x06 / 0x20  0x001CFCFC
 *   QCA6410   0x06 / 0x21  0x001B58EC
 *   QCA6411   0x06 / 0x21  0x001B58BC
 *   QCA7006AQ 0x06 / 0x21  0x001B58AC
 *   QCA7000   0x06 / 0x22  0x001B589C
 *   QCA7000   0x06 / 0x22  0x001B58DC
 *   QCA7005   0x06 / 0x22  0x001B587C
 *   QCA7500   0x06 / 0x30  0x001D4C0F
 *
 *   some chipsets have have multiple STRAP field values; this is
 *   not an error; there may be multiple versions of a chipset;
 *
 *--------------------------------------------------------------------*/

void chipset (void const * memory, uint32_t * ident)

{

#ifndef __GNUC__
#pragma pack (push,1)
#endif

	struct __packed vs_sw_ver_confirm
	{
		struct ethernet_hdr ethernet;
		struct qualcomm_hdr qualcomm;
		uint8_t MSTATUS;
		uint8_t MDEVICE_CLASS;
		uint8_t MVERLENGTH;
		char MVERSION [254];
	}
	* confirm = (struct vs_sw_ver_confirm *) (memory);
	struct __packed chipinfo
	{
		uint8_t RESVD;
		uint32_t STRAP;
		uint32_t STEP_NUMBER;
	}
	* chipinfo = (struct chipinfo *) (& confirm->MVERSION [64]);
	typedef struct __packed
	{
		uint32_t STRAP;
		uint8_t CLASS;
		uint8_t DEVICE;
	}
	chipdata;

#ifndef __GNUC__
#pragma pack (pop)
#endif

	chipdata bootrom [] =
	{
		{
			0x00000042,
			0x01,
			CHIPSET_INT6000A1
		},
		{
			0x00006300,
			0x01,
			CHIPSET_INT6300A0
		},
		{
			0x00006400,
			0x03,
			CHIPSET_INT6400A0
		},
		{
			0x00007400,
			0x03,
			CHIPSET_AR7400A0
		},
		{
			0x0F001D1A,
			0x03,
			CHIPSET_QCA7450A0
		},
		{
			0x0E001D1A,
			0x03,
			CHIPSET_QCA7451A0
		},
		{
			0x001CFC00,
			0x05,
			CHIPSET_QCA7420A0
		},
		{
			0x001CFCFC,
			0x05,
			CHIPSET_QCA7420A0
		},
		{
			0x001CFCFC,
			0x06,
			CHIPSET_QCA7420A0
		},
		{
			0x001B58EC,
			0x06,
			CHIPSET_QCA6410A0
		},
		{
			0x001B58AC,
			0x06,
			CHIPSET_QCA7006AQA0
		},
		{
			0x001B58BC,
			0x06,
			CHIPSET_QCA6411A0
		},
		{
			0x001B58DC,
			0x06,
			CHIPSET_QCA7000A0
		},
		{
			0x001B587C,
			0x06,
			CHIPSET_QCA7005A0
		},
		{
			0x001D4C00,
			0x06,
			CHIPSET_QCA7500A0
		},
		{
			0x001D4C0F,
			0x06,
			CHIPSET_QCA7500A0
		}
	};
	chipdata firmware [] =
	{
		{
			0x00000000,
			0x01,
			CHIPSET_INT6000A1
		},
		{
			0x00000000,
			0x02,
			CHIPSET_INT6300A0
		},
		{
			0x00000000,
			0x03,
			CHIPSET_INT6400A0
		},
		{
			0x00000000,
			0x05,
			CHIPSET_AR6405A0
		},
		{
			0x00000000,
			0x04,
			CHIPSET_AR7400A0
		},
		{
			0x0F001D1A,
			0x20,
			CHIPSET_QCA7450A0
		},
		{
			0x0E001D1A,
			0x20,
			CHIPSET_QCA7451A0
		},
		{
			0x001CFC00,
			0x20,
			CHIPSET_QCA7420A0
		},
		{
			0x001CFCFC,
			0x20,
			CHIPSET_QCA7420A0
		},
		{
			0x001B587C,
			0x22,
			CHIPSET_QCA7005A0
		},
		{
			0x001B58AC,
			0x21,
			CHIPSET_QCA7006AQA0
		},
		{
			0x001B58EC,
			0x21,
			CHIPSET_QCA6410A0
		},
		{
			0x001B58BC,
			0x21,
			CHIPSET_QCA6411A0
		},
		{
			0x001B58DC,
			0x22,
			CHIPSET_QCA7000A0
		},
		{
			0x001D4C00,
			0x30,
			CHIPSET_QCA7500A0
		},
		{
			0x001D4C0F,
			0x30,
			CHIPSET_QCA7500A0
		}
	};
	unsigned chip;
	if (! strcmp (confirm->MVERSION, "BootLoader"))
	{
		for (chip = 0; chip < SIZEOF (bootrom); chip++)
		{
			if (bootrom [chip].CLASS != confirm->MDEVICE_CLASS)
			{
				continue;
			}
			if (bootrom [chip].STRAP != LE32TOH (chipinfo->STRAP))
			{
				continue;
			}
			confirm->MDEVICE_CLASS = bootrom [chip].DEVICE;
			* ident = LE32TOH (chipinfo->STRAP);
			return;
		}
	}
	else
	{
		for (chip = 0; chip < SIZEOF (firmware); chip++)
		{
			if (firmware [chip].CLASS != confirm->MDEVICE_CLASS)
			{
				continue;
			}
			if (firmware [chip].STRAP < CHIPSET_PANTHER_LYNX)
			{
				confirm->MDEVICE_CLASS = firmware [chip].DEVICE;
				* ident = LE32TOH (chipinfo->STRAP);
				return;
			}
			chipinfo = (struct chipinfo *) (& confirm->MVERSION [64]);
			if (firmware [chip].STRAP == LE32TOH (chipinfo->STRAP))
			{
				confirm->MDEVICE_CLASS = firmware [chip].DEVICE;
				* ident = LE32TOH (chipinfo->STRAP);
				return;
			}
			chipinfo = (struct chipinfo *) (& confirm->MVERSION [128]);
			if (firmware [chip].STRAP == LE32TOH (chipinfo->STRAP))
			{
				confirm->MDEVICE_CLASS = firmware [chip].DEVICE;
				* ident = LE32TOH (chipinfo->STRAP);
				return;
			}
			chipinfo = (struct chipinfo *) (& confirm->MVERSION [253]);
			if (firmware [chip].STRAP == LE32TOH (chipinfo->STRAP))
			{
				confirm->MDEVICE_CLASS = firmware [chip].DEVICE;
				* ident = LE32TOH (chipinfo->STRAP);
				return;
			}
		}
	}
	return;
}

#endif



