/*
 * quanta_ly8_platform.c - Quanta LY8 Platform Support
 *
 * Copyright (C) 2014, 2020 Cumulus Networks, Inc.
 * Original author: Puneet Shenoy (puneet@cumulusnetworks.com)
 * Latest author: Andy Rao (arao@cumulusnetworks.com)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 *  02110-1301, USA.
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/platform_data/at24.h>
#include <linux/i2c/pca954x.h>
#include <linux/platform_data/pca953x.h>
#include <linux/platform_device.h>
#include <linux/i2c/sff-8436.h>

#include "platform_defs.h"

#define DRIVER_NAME	"quanta_ly8_rangeley_platform"
#define DRIVER_VERSION	"1.1"
#define SMB_I801_NAME "SMBus I801 adapter"
#define LY8_I2C_I801_BUS 0
#define PCA_9555_GPIO_COUNT 16
#define PCA_9698_GPIO_COUNT 40
#define LY8_PORT_COUNT 54

static struct platform_driver ly8_platform_driver;

mk_eeprom(spd1,  52, 256,  AT24_FLAG_READONLY | AT24_FLAG_IRUGO);
mk_eeprom(cpu, 53, 256, AT24_FLAG_IRUGO);
mk_eeprom(board, 54, 256, AT24_FLAG_IRUGO);

struct ly8_device_info {
	int bus;
	struct i2c_board_info info;
	int has_port;  /* 1 for SFP, 2 for QSFP */
	int port_base;
	int port_bus;
	int num_ports;
};

struct ly8_gpio_pin_info {
	char *name;
	int direction; // 1 = out
	int value;
	int nc;
};

struct port_info {
	int type; /* 0 = SFP , 1 = QSFP */
	struct i2c_board_info *b;
	struct i2c_client *c;
};
static struct port_info ports_info[LY8_PORT_COUNT];

/*  I2C Muxes  */
#define ly8_pca9546(addr, busno)					\
	enum {								\
		LY8_PCA9546_##addr##_0 = busno,				\
		LY8_PCA9546_##addr##_1,					\
		LY8_PCA9546_##addr##_2,					\
		LY8_PCA9546_##addr##_3,					\
	};								\
	static struct pca954x_platform_mode ly8_mode_pca9546_##addr [] = { \
		{ .adap_id = LY8_PCA9546_##addr##_0, .deselect_on_exit = 1,}, \
		{ .adap_id = LY8_PCA9546_##addr##_1, .deselect_on_exit = 1,}, \
		{ .adap_id = LY8_PCA9546_##addr##_2, .deselect_on_exit = 1,}, \
		{ .adap_id = LY8_PCA9546_##addr##_3, .deselect_on_exit = 1,},			\
	};								\
	static struct pca954x_platform_data ly8_data_pca9546_##addr = { \
		.modes = ly8_mode_pca9546_##addr,			\
		.num_modes = ARRAY_SIZE(ly8_mode_pca9546_##addr),	\
	};


#define ly8_pca9548(addr, busno)					\
	enum {								\
		LY8_PCA9548_##addr##_0 = busno,				\
		LY8_PCA9548_##addr##_1,					\
		LY8_PCA9548_##addr##_2,					\
		LY8_PCA9548_##addr##_3,					\
		LY8_PCA9548_##addr##_4,					\
		LY8_PCA9548_##addr##_5,					\
		LY8_PCA9548_##addr##_6,					\
		LY8_PCA9548_##addr##_7,					\
	};								\
	static struct pca954x_platform_mode ly8_mode_pca9548_##addr [] = { \
		{ .adap_id = LY8_PCA9548_##addr##_0, .deselect_on_exit = 1,}, \
		{ .adap_id = LY8_PCA9548_##addr##_1, .deselect_on_exit = 1,}, \
		{ .adap_id = LY8_PCA9548_##addr##_2, .deselect_on_exit = 1,}, \
		{ .adap_id = LY8_PCA9548_##addr##_3, .deselect_on_exit = 1,}, \
		{ .adap_id = LY8_PCA9548_##addr##_4, .deselect_on_exit = 1,}, \
		{ .adap_id = LY8_PCA9548_##addr##_5, .deselect_on_exit = 1,}, \
		{ .adap_id = LY8_PCA9548_##addr##_6, .deselect_on_exit = 1,}, \
		{ .adap_id = LY8_PCA9548_##addr##_7, .deselect_on_exit = 1,}, \
	};								\
	static struct pca954x_platform_data ly8_data_pca9548_##addr = { \
		.modes = ly8_mode_pca9548_##addr,			\
		.num_modes = ARRAY_SIZE(ly8_mode_pca9548_##addr),	\
	};

/* I2C I801 -> PCA9546(0x71) */
ly8_pca9546(71, 10);
/* I2C I801 -> PCA9546_1(0x72) */
ly8_pca9546(72, 14)
/* I2C I801 -> PCA9548_1(0x77) */
ly8_pca9548(77, 18)
/* I2C I801 -> PCA9548_1_1(0x77) -> PCA9548_1_2 (0x73)*/
ly8_pca9548(1_73, 26)
/* I2C I801 -> PCA9548_1_1(0x77) -> PCA9548_1_2 (0x74)*/
ly8_pca9548(1_74, 34)
/* I2C I801 -> PCA9548_1_1(0x77) -> PCA9548_1_2 (0x76)*/
ly8_pca9548(1_76, 42)
/* I2C I801 -> PCA9548_2_1(0x77) -> PCA9548_2_2 (0x73)*/
ly8_pca9548(2_73, 50)
/* I2C I801 -> PCA9548_2_1(0x77) -> PCA9548_2_2 (0x74)*/
ly8_pca9548(2_74, 58)
/* I2C I801 -> PCA9548_2_1(0x77) -> PCA9548_2_2 (0x75)*/
ly8_pca9548(2_75, 66)
/* I2C I801 -> PCA9548_2_1(0x77) -> CH5 -> PCA9548_8 (0x76)*/
ly8_pca9548(2_76, 74)
/* I2C I801 -> PCA9546_1(0x72)  -> PCA9546(0x76)*/
ly8_pca9546(76, 82)

/* GPIOs */
#define LY8_GPIO_BASE 100
#define LY8_GPIO_24_BASE LY8_GPIO_BASE
#define LY8_GPIO_23_BASE (LY8_GPIO_24_BASE + PCA_9555_GPIO_COUNT)
#define LY8_GPIO_26_BASE (LY8_GPIO_23_BASE + PCA_9555_GPIO_COUNT)
#define LY8_GPIO_23_2_BASE (LY8_GPIO_26_BASE + PCA_9555_GPIO_COUNT)
#define LY8_GPIO_24_2_BASE (LY8_GPIO_23_2_BASE + PCA_9555_GPIO_COUNT)
#define LY8_GPIO_23_3_BASE (LY8_GPIO_24_2_BASE + PCA_9555_GPIO_COUNT)
#define LY8_GPIO_20_BASE (LY8_GPIO_23_3_BASE + PCA_9555_GPIO_COUNT)
#define LY8_GPIO_SFP_1_8_BASE (LY8_GPIO_20_BASE + PCA_9555_GPIO_COUNT)
#define LY8_GPIO_SFP_9_16_BASE (LY8_GPIO_SFP_1_8_BASE + PCA_9698_GPIO_COUNT)
#define LY8_GPIO_SFP_17_24_BASE (LY8_GPIO_SFP_9_16_BASE + PCA_9698_GPIO_COUNT)
#define LY8_GPIO_SFP_25_32_BASE (LY8_GPIO_SFP_17_24_BASE + PCA_9698_GPIO_COUNT)
#define LY8_GPIO_SFP_33_40_BASE (LY8_GPIO_SFP_25_32_BASE + PCA_9698_GPIO_COUNT)
#define LY8_GPIO_SFP_41_48_BASE (LY8_GPIO_SFP_33_40_BASE + PCA_9698_GPIO_COUNT)

static struct ly8_gpio_pin_info ly8_gpio_24_info[PCA_9555_GPIO_COUNT] = {
	{.name = "qsfp_pwr_enable", .direction = 1, .value = 1,},
	{.name = "9555_5_int",},
	{.name = "9555_4_int",},
	{.name = "bcm5461s_int",},
	{.name = "fan1_present",},
	{.name = "fan2_present",},
	{.name = "fan3_present",},
	{.name = "m21441_rst", .direction = 1, .value = 1,},
	{.name = "fan1_f2b",},
	{.name = "fan2_f2b",},
	{.name = "fan3_f2b",},
	{.name = "nc",},
	{.name = "fan1_led",},
	{.name = "fan2_led",},
	{.name = "fan3_led",},
	{.name = "qsfp_int",},
};

static const char *ly8_gpio_24_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_24_data = {
	.gpio_base = LY8_GPIO_24_BASE,
	.names = ly8_gpio_24_names,
};

static struct ly8_gpio_pin_info ly8_gpio_23_info[PCA_9555_GPIO_COUNT] = {
	{.name = "fan0_board",},
	{.name = "fan1_board",},
	{.name = "fan2_board",},
	{.name = "fan3_board",},
	{.name = "power_good",},
	{.name = "sfp_power_enable", .direction = 1, .value = 1,},
	{.name = "qsfp_present",     .direction = 1, .value = 1,},
	{.name = "usb_reset",        .direction = 1, .value = 1,},
	{.name = "pca9554_int",},
	{.name = "mgmt_present",},
	{.name = "board_id_0",},
	{.name = "board_id_1",},
	{.name = "board_id_2",},
	{.name = "board_id_3",},
	{.name = "board_id_4",},
	{.name = "board_id_5",},
};
static const char *ly8_gpio_23_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_23_data = {
	.gpio_base = LY8_GPIO_23_BASE,
	.names = ly8_gpio_23_names,
};

static struct ly8_gpio_pin_info ly8_gpio_26_info[PCA_9555_GPIO_COUNT] = {
	{.name = "psu_pwr1_present",},
	{.name = "psu_pwr1_dc_ok",},
	{.name = "psu_pwr1_int",},
	{.name = "psu_pwr2_present",},
	{.name = "psu_pwr2_dc_ok",},
	{.name = "psu_pwr2_int",},
	{.name = "psu_pwr1_ac_ok",},
	{.name = "psu_pwr2_ac_ok",},
	{.name = "psu_pwr1_reset", .direction = 1, .value = 0,},
	{.name = "psu_pwr2_reset", .direction = 1, .value = 0,},
	{.name = "psu1_green_led", .direction = 1, .value = 1,},
	{.name = "psu1_red_led",   .direction = 1, .value = 0,},
	{.name = "psu2_green_led", .direction = 1, .value = 1,},
	{.name = "psu2_red_led",   .direction = 1, .value = 0,},
	{.name = "fan_green_led",  .direction = 1, .value = 1,},
	{.name = "fan_red_led",    .direction = 1, .value = 0,},
};
static const char *ly8_gpio_26_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_26_data = {
	.gpio_base = LY8_GPIO_26_BASE,
	.names = ly8_gpio_26_names,
};

static struct ly8_gpio_pin_info ly8_gpio_23_2_info[PCA_9555_GPIO_COUNT] = {
	{.name = "qsfp49_reset", .direction = 1, .value = 1,},
	{.name = "qsfp49_intr",},
	{.name = "qsfp49_present",},
	{.name = "qsfp49_lpmode", .direction = 1, .value = 1,},
	{.name = "qsfp50_reset", .direction = 1, .value = 1,},
	{.name = "qsfp50_intr",},
	{.name = "qsfp50_present",},
	{.name = "qsfp50_lpmode", .direction = 1, .value = 1,},
	{.name = "qsfp51_reset", .direction = 1, .value = 1,},
	{.name = "qsfp51_intr",},
	{.name = "qsfp51_present",},
	{.name = "qsfp51_lpmode", .direction = 1, .value = 1,},
	{.name = "qsfp52_reset", .direction = 1, .value = 1,},
	{.name = "qsfp52_intr",},
	{.name = "qsfp52_present",},
	{.name = "qsfp52_lpmode", .direction = 1, .value = 1,},
};
static const char *ly8_gpio_23_2_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_23_2_data = {
	.gpio_base = LY8_GPIO_23_2_BASE,
	.names = ly8_gpio_23_2_names,
};

static struct ly8_gpio_pin_info ly8_gpio_24_2_info[PCA_9555_GPIO_COUNT] = {
	{.name = "port49_40_led", },
	{.name = "port49_10_led", },
	{.name = "port50_40_led", },
	{.name = "port50_10_led", },
	{.name = "port51_40_led", },
	{.name = "port51_10_led", },
	{.name = "port52_40_led", },
	{.name = "port52_10_led", },
	{.name = "nc_1",},
	{.name = "nc_2",},
	{.name = "nc_3",},
	{.name = "nc_4",},
	{.name = "nc_5",},
	{.name = "nc_6",},
	{.name = "nc_7",},
	{.name = "nc_8",},
};
static const char *ly8_gpio_24_2_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_24_2_data = {
	.gpio_base = LY8_GPIO_24_2_BASE,
	.names = ly8_gpio_24_2_names,
};

static struct ly8_gpio_pin_info ly8_gpio_23_3_info[PCA_9555_GPIO_COUNT] = {
	{.name = "qsfp53_reset", .direction = 1, .value = 1,},
	{.name = "qsfp53_intr",},
	{.name = "qsfp53_present",},
	{.name = "qsfp53_lpmode", .direction = 1, .value = 1,},
	{.name = "qsfp54_reset", .direction = 1, .value = 1,},
	{.name = "qsfp54_intr",},
	{.name = "qsfp54_present",},
	{.name = "qsfp54_lpmode", .direction = 1, .value = 1,},
	{.name = "qsfp_power_enable", .direction = 1, .value = 0,},
	{.name = "sys_reset", .direction = 1, .value = 1,},
	{.name = "9546_reset", .direction = 1, .value = 1,},
	{.name = "bcm84328_reset", .direction = 1, .value = 1,},
	{.name = "nc_9",},
	{.name = "qdb_id_0",},
	{.name = "qdb_id_1",},
	{.name = "qdb_id_2",},
};

static const char *ly8_gpio_23_3_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_23_3_data = {
	.gpio_base = LY8_GPIO_23_3_BASE,
	.names = ly8_gpio_23_3_names,
};

static struct ly8_gpio_pin_info ly8_gpio_20_info[PCA_9555_GPIO_COUNT] = {
	{.name = "mb_i2c_rst", .direction = 1, .value = 1,},
	{.name = "mb_led_rst", .direction = 1, .value = 1,},
	{.name = "mb_eth_rst", .direction = 1, .value = 1,},
	{.name = "mb_sw_rst", .direction = 1, .value = 1,},
	{.name = "mb_odd_phy_rst", .direction = 1, .value = 1,},
	{.name = "mb_even_phy_rst", .direction = 1, .value = 1,},
	{.name = "alta_rst", .direction = 1, .value = 1,},
	{.name = "usb_rst", .direction = 1, .value = 1,},

	{.name = "alta_power", .direction = 1, .value = 1,},
	{.name = "i2c_mac_switch", .direction = 1, .value = 1,},
	{.name = "boot_led", .direction = 1, .value = 0,},
	{.name = "sys_led", .direction = 1, .value = 1,},
	{.name = "board_pwr_led", .direction = 1, .value = 1,},
	{.name = "nc_10",},
	{.name = "nc_11",},
	{.name = "nc_12",},
};

static const char *ly8_gpio_20_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_20_data = {
	.gpio_base = LY8_GPIO_20_BASE,
	.names = ly8_gpio_20_names,
};

static const char *ly8_gpio_sfp_1_8_names[PCA_9698_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_sfp_1_8_data = {
	.gpio_base = LY8_GPIO_SFP_1_8_BASE,
	.names = ly8_gpio_sfp_1_8_names,
};
static struct ly8_gpio_pin_info ly8_gpio_sfp_1_8_info[PCA_9698_GPIO_COUNT] = {
	{.name = "sfp1_present_l"},
	{.name = "sfp1_los"},
	{.name = "sfp1_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp1_tx_fault"},
	{.name = "sfp2_present_l"},
	{.name = "sfp2_los"},
	{.name = "sfp2_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp2_tx_fault"},
	{.name = "sfp3_present_l"},
	{.name = "sfp3_los"},
	{.name = "sfp3_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp3_tx_fault"},
	{.name = "sfp4_present_l"},
	{.name = "sfp4_los"},
	{.name = "sfp4_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp4_tx_fault"},
	{.name = "sfp5_present_l"},
	{.name = "sfp5_los"},
	{.name = "sfp5_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp5_tx_fault"},
	{.name = "sfp6_present_l"},
	{.name = "sfp6_los"},
	{.name = "sfp6_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp6_tx_fault"},
	{.name = "sfp7_present_l"},
	{.name = "sfp7_los"},
	{.name = "sfp7_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp7_tx_fault"},
	{.name = "sfp8_present_l"},
	{.name = "sfp8_los"},
	{.name = "sfp8_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp8_tx_fault"},
	{.name = "nc_sfp_1_0", .nc = 1},
	{.name = "nc_sfp_1_1", .nc = 1},
	{.name = "nc_sfp_1_2", .nc = 1},
	{.name = "nc_sfp_1_3", .nc = 1},
	{.name = "nc_sfp_1_4", .nc = 1},
	{.name = "nc_sfp_1_5", .nc = 1},
	{.name = "nc_sfp_1_6", .nc = 1},
	{.name = "nc_sfp_1_7", .nc = 1},
};

static const char *ly8_gpio_sfp_9_16_names[PCA_9698_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_sfp_9_16_data = {
	.gpio_base = LY8_GPIO_SFP_9_16_BASE,
	.names = ly8_gpio_sfp_9_16_names,
};
static struct ly8_gpio_pin_info ly8_gpio_sfp_9_16_info[PCA_9698_GPIO_COUNT] = {
	{.name = "sfp9_present_l"},
	{.name = "sfp9_los"},
	{.name = "sfp9_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp9_tx_fault"},
	{.name = "sfp10_present_l"},
	{.name = "sfp10_los"},
	{.name = "sfp10_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp10_tx_fault"},
	{.name = "sfp11_present_l"},
	{.name = "sfp11_los"},
	{.name = "sfp11_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp11_tx_fault"},
	{.name = "sfp12_present_l"},
	{.name = "sfp12_los"},
	{.name = "sfp12_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp12_tx_fault"},
	{.name = "sfp13_present_l"},
	{.name = "sfp13_los"},
	{.name = "sfp13_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp13_tx_fault"},
	{.name = "sfp14_present_l"},
	{.name = "sfp14_los"},
	{.name = "sfp14_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp14_tx_fault"},
	{.name = "sfp15_present_l"},
	{.name = "sfp15_los"},
	{.name = "sfp15_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp15_tx_fault"},
	{.name = "sfp16_present_l"},
	{.name = "sfp16_los"},
	{.name = "sfp16_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp16_tx_fault"},
	{.name = "nc_sfp_9_0", .nc = 1},
	{.name = "nc_sfp_9_1", .nc = 1},
	{.name = "nc_sfp_9_2", .nc = 1},
	{.name = "nc_sfp_9_3", .nc = 1},
	{.name = "nc_sfp_9_4", .nc = 1},
	{.name = "nc_sfp_9_5", .nc = 1},
	{.name = "nc_sfp_9_6", .nc = 1},
	{.name = "nc_sfp_9_7", .nc = 1},
};

static const char *ly8_gpio_sfp_17_24_names[PCA_9698_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_sfp_17_24_data = {
	.gpio_base = LY8_GPIO_SFP_17_24_BASE,
	.names = ly8_gpio_sfp_17_24_names,
};
static struct ly8_gpio_pin_info ly8_gpio_sfp_17_24_info[PCA_9698_GPIO_COUNT] = {
	{.name = "sfp17_present_l"},
	{.name = "sfp17_los"},
	{.name = "sfp17_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp17_tx_fault"},
	{.name = "sfp18_present_l"},
	{.name = "sfp18_los"},
	{.name = "sfp18_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp18_tx_fault"},
	{.name = "sfp19_present_l"},
	{.name = "sfp19_los"},
	{.name = "sfp19_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp19_tx_fault"},
	{.name = "sfp20_present_l"},
	{.name = "sfp20_los"},
	{.name = "sfp20_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp20_tx_fault"},
	{.name = "sfp21_present_l"},
	{.name = "sfp21_los"},
	{.name = "sfp21_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp21_tx_fault"},
	{.name = "sfp22_present_l"},
	{.name = "sfp22_los"},
	{.name = "sfp22_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp22_tx_fault"},
	{.name = "sfp23_present_l"},
	{.name = "sfp23_los"},
	{.name = "sfp23_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp23_tx_fault"},
	{.name = "sfp24_present_l"},
	{.name = "sfp24_los"},
	{.name = "sfp24_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp24_tx_fault"},
	{.name = "nc_sfp_17_0", .nc = 1},
	{.name = "nc_sfp_17_1", .nc = 1},
	{.name = "nc_sfp_17_2", .nc = 1},
	{.name = "nc_sfp_17_3", .nc = 1},
	{.name = "nc_sfp_17_4", .nc = 1},
	{.name = "nc_sfp_17_5", .nc = 1},
	{.name = "nc_sfp_17_6", .nc = 1},
	{.name = "nc_sfp_17_7", .nc = 1},
};

static const char *ly8_gpio_sfp_25_32_names[PCA_9698_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_sfp_25_32_data = {
	.gpio_base = LY8_GPIO_SFP_25_32_BASE,
	.names = ly8_gpio_sfp_25_32_names,
};
static struct ly8_gpio_pin_info ly8_gpio_sfp_25_32_info[PCA_9698_GPIO_COUNT] = {
	{.name = "sfp25_present_l"},
	{.name = "sfp25_los"},
	{.name = "sfp25_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp25_tx_fault"},
	{.name = "sfp26_present_l"},
	{.name = "sfp26_los"},
	{.name = "sfp26_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp26_tx_fault"},
	{.name = "sfp27_present_l"},
	{.name = "sfp27_los"},
	{.name = "sfp27_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp27_tx_fault"},
	{.name = "sfp28_present_l"},
	{.name = "sfp28_los"},
	{.name = "sfp28_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp28_tx_fault"},
	{.name = "sfp29_present_l"},
	{.name = "sfp29_los"},
	{.name = "sfp29_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp29_tx_fault"},
	{.name = "sfp30_present_l"},
	{.name = "sfp30_los"},
	{.name = "sfp30_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp30_tx_fault"},
	{.name = "sfp31_present_l"},
	{.name = "sfp31_los"},
	{.name = "sfp31_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp31_tx_fault"},
	{.name = "sfp32_present_l"},
	{.name = "sfp32_los"},
	{.name = "sfp32_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp32_tx_fault"},
	{.name = "nc_sfp_25_0", .nc = 1},
	{.name = "nc_sfp_25_1", .nc = 1},
	{.name = "nc_sfp_25_2", .nc = 1},
	{.name = "nc_sfp_25_3", .nc = 1},
	{.name = "nc_sfp_25_4", .nc = 1},
	{.name = "nc_sfp_25_5", .nc = 1},
	{.name = "nc_sfp_25_6", .nc = 1},
	{.name = "nc_sfp_25_7", .nc = 1},
};

static const char *ly8_gpio_sfp_33_40_names[PCA_9698_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_sfp_33_40_data = {
	.gpio_base = LY8_GPIO_SFP_33_40_BASE,
	.names = ly8_gpio_sfp_33_40_names,
};
static struct ly8_gpio_pin_info ly8_gpio_sfp_33_40_info[PCA_9698_GPIO_COUNT] = {
	{.name = "sfp33_present_l"},
	{.name = "sfp33_los"},
	{.name = "sfp33_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp33_tx_fault"},
	{.name = "sfp34_present_l"},
	{.name = "sfp34_los"},
	{.name = "sfp34_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp34_tx_fault"},
	{.name = "sfp35_present_l"},
	{.name = "sfp35_los"},
	{.name = "sfp35_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp35_tx_fault"},
	{.name = "sfp36_present_l"},
	{.name = "sfp36_los"},
	{.name = "sfp36_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp36_tx_fault"},
	{.name = "sfp37_present_l"},
	{.name = "sfp37_los"},
	{.name = "sfp37_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp37_tx_fault"},
	{.name = "sfp38_present_l"},
	{.name = "sfp38_los"},
	{.name = "sfp38_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp38_tx_fault"},
	{.name = "sfp39_present_l"},
	{.name = "sfp39_los"},
	{.name = "sfp39_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp39_tx_fault"},
	{.name = "sfp40_present_l"},
	{.name = "sfp40_los"},
	{.name = "sfp40_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp40_tx_fault"},
	{.name = "nc_sfp_33_0", .nc = 1},
	{.name = "nc_sfp_33_1", .nc = 1},
	{.name = "nc_sfp_33_2", .nc = 1},
	{.name = "nc_sfp_33_3", .nc = 1},
	{.name = "nc_sfp_33_4", .nc = 1},
	{.name = "nc_sfp_33_5", .nc = 1},
	{.name = "nc_sfp_33_6", .nc = 1},
	{.name = "nc_sfp_33_7", .nc = 1},
};

static const char *ly8_gpio_sfp_41_48_names[PCA_9698_GPIO_COUNT];
static struct pca953x_platform_data ly8_gpio_sfp_41_48_data = {
	.gpio_base = LY8_GPIO_SFP_41_48_BASE,
	.names = ly8_gpio_sfp_41_48_names,
};
static struct ly8_gpio_pin_info ly8_gpio_sfp_41_48_info[PCA_9698_GPIO_COUNT] = {
	{.name = "sfp41_present_l"},
	{.name = "sfp41_los"},
	{.name = "sfp41_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp41_tx_fault"},
	{.name = "sfp42_present_l"},
	{.name = "sfp42_los"},
	{.name = "sfp42_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp42_tx_fault"},
	{.name = "sfp43_present_l"},
	{.name = "sfp43_los"},
	{.name = "sfp43_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp43_tx_fault"},
	{.name = "sfp44_present_l"},
	{.name = "sfp44_los"},
	{.name = "sfp44_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp44_tx_fault"},
	{.name = "sfp45_present_l"},
	{.name = "sfp45_los"},
	{.name = "sfp45_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp45_tx_fault"},
	{.name = "sfp46_present_l"},
	{.name = "sfp46_los"},
	{.name = "sfp46_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp46_tx_fault"},
	{.name = "sfp47_present_l"},
	{.name = "sfp47_los"},
	{.name = "sfp47_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp47_tx_fault"},
	{.name = "sfp48_present_l"},
	{.name = "sfp48_los"},
	{.name = "sfp48_tx_disable", .direction = 1, .value = 1},
	{.name = "sfp48_tx_fault"},
	{.name = "nc_sfp_41_0", .nc = 1},
	{.name = "nc_sfp_41_1", .nc = 1},
	{.name = "nc_sfp_41_2", .nc = 1},
	{.name = "nc_sfp_41_3", .nc = 1},
	{.name = "nc_sfp_41_4", .nc = 1},
	{.name = "nc_sfp_41_5", .nc = 1},
	{.name = "nc_sfp_41_6", .nc = 1},
	{.name = "nc_sfp_41_7", .nc = 1},
};

/* I2C Device Map */
static struct ly8_device_info i2c_devices_level1[] = {
	{
		.bus = LY8_I2C_I801_BUS,       /* SPD DIMM EEPROM */
		{I2C_BOARD_INFO("spd", 0x52),
		 .platform_data = &spd1_52_at24,},
	},
	{
		.bus = LY8_I2C_I801_BUS,
		{I2C_BOARD_INFO("pca9546", 0x71),
		 .platform_data = &ly8_data_pca9546_71,},
	},
	{
		.bus = LY8_I2C_I801_BUS,
		{I2C_BOARD_INFO("pca9546", 0x72),
		 .platform_data = &ly8_data_pca9546_72,},
	},
	{
		.bus = LY8_I2C_I801_BUS,     /* Sensors Chip */
		{I2C_BOARD_INFO("CY8C3245R1", 0x4e),},
	},
	{
		.bus = LY8_I2C_I801_BUS,
		{I2C_BOARD_INFO("pca9548", 0x77),
		 .platform_data = &ly8_data_pca9548_77,},
	},

};
static struct ly8_device_info i2c_devices_level3[] = {
	{
		.bus = LY8_PCA9548_2_76_4,
		{I2C_BOARD_INFO("dummy", 0x21),}, /* M21441 Chip for Port 53 */
	},
};
static struct ly8_device_info i2c_devices_level2[] = {
	/* Following devices are on CPU Board */
	{
		.bus = LY8_PCA9546_71_0,
		{I2C_BOARD_INFO("pca9555", 0x20),
		.platform_data = &ly8_gpio_20_data},
	},
	{
		.bus = LY8_PCA9546_71_1,
		{I2C_BOARD_INFO("24c02", 0x53),
		 .platform_data = &cpu_53_at24,},
	},
	/* Following devices are on MB */
	{
		.bus = LY8_PCA9546_72_0, /* PSU1 PMBUS */
		{I2C_BOARD_INFO("ps2471", 0x6f),},
	},
	{
		.bus = LY8_PCA9546_72_1, /* PSU2 PMBUS */
		{I2C_BOARD_INFO("ps2471", 0x69),},
	},
	{
		.bus = LY8_PCA9546_72_2,
		{I2C_BOARD_INFO("pca9555", 0x26),
		.platform_data = &ly8_gpio_26_data},
	},
	{
		.bus = LY8_PCA9546_72_3,   /* Board EEPROM  */
		{I2C_BOARD_INFO("24c02", 0x54),
		 .platform_data = &board_54_at24,},
	},
	{
		.bus = LY8_PCA9548_77_0,
		{I2C_BOARD_INFO("pca9555", 0x24),
		.platform_data = &ly8_gpio_24_data},
	},
	{
		.bus = LY8_PCA9548_77_0,
		{I2C_BOARD_INFO("pca9555", 0x23),
		 .platform_data = &ly8_gpio_23_data},
	},
	{
		.bus = LY8_PCA9548_77_1,
		{I2C_BOARD_INFO("pca9548", 0x73),
		 .platform_data = &ly8_data_pca9548_1_73,},
		 .has_port = 1, .port_base = 1,
		 .port_bus = LY8_PCA9548_1_73_0,
  		 .num_ports = 8,
	},
	{
		.bus = LY8_PCA9548_77_1,
		{I2C_BOARD_INFO("pca9548", 0x74),
		 .platform_data = &ly8_data_pca9548_1_74,},
		 .has_port = 1, .port_base = 9,
		 .port_bus = LY8_PCA9548_1_74_0,
  		 .num_ports = 8,
	},
	{
		.bus = LY8_PCA9548_77_1,
		{I2C_BOARD_INFO("pca9548", 0x76),
		 .platform_data = &ly8_data_pca9548_1_76,},
		 .has_port = 1, .port_base = 17,
		 .port_bus = LY8_PCA9548_1_76_0,
  		 .num_ports = 8,
	},
	{
		.bus = LY8_PCA9548_77_2,
		{I2C_BOARD_INFO("pca9548", 0x73),
		 .platform_data = &ly8_data_pca9548_2_73,},
		 .has_port = 1, .port_base = 25,
		 .port_bus = LY8_PCA9548_2_73_0,
  		 .num_ports = 8,
	},
	{
		.bus = LY8_PCA9548_77_2,
		{I2C_BOARD_INFO("pca9548", 0x74),
		 .platform_data = &ly8_data_pca9548_2_74,},
		 .has_port = 1, .port_base = 33,
		 .port_bus = LY8_PCA9548_2_74_0,
  		 .num_ports = 8,
	},
	{
		.bus = LY8_PCA9548_77_2,
		{I2C_BOARD_INFO("pca9548", 0x75),
		 .platform_data = &ly8_data_pca9548_2_75,},
		 .has_port = 1, .port_base = 41,
		 .port_bus = LY8_PCA9548_2_75_0,
  		 .num_ports = 8,
	},
	{
		.bus = LY8_PCA9548_77_3,
		{I2C_BOARD_INFO("pca9698", 0x23),
		 .platform_data = &ly8_gpio_sfp_1_8_data},
	},
	{
		.bus = LY8_PCA9548_77_3,
		{I2C_BOARD_INFO("pca9698", 0x21),
		 .platform_data = &ly8_gpio_sfp_9_16_data},
	},
	{
		.bus = LY8_PCA9548_77_3,
		{I2C_BOARD_INFO("pca9698", 0x22),
		 .platform_data = &ly8_gpio_sfp_17_24_data},
	},
	{
		.bus = LY8_PCA9548_77_4,
		{I2C_BOARD_INFO("pca9698", 0x23),
		 .platform_data = &ly8_gpio_sfp_25_32_data},
	},
	{
		.bus = LY8_PCA9548_77_4,
		{I2C_BOARD_INFO("pca9698", 0x24),
		 .platform_data = &ly8_gpio_sfp_33_40_data},
	},
	{
		.bus = LY8_PCA9548_77_4,
		{I2C_BOARD_INFO("pca9698", 0x25),
		 .platform_data = &ly8_gpio_sfp_41_48_data},
	},
	{
		.bus = LY8_PCA9548_77_5,
		{I2C_BOARD_INFO("pca9548", 0x76),
		 .platform_data = &ly8_data_pca9548_2_76,},
		 .has_port = 2, .port_base = 49,
		 .port_bus = LY8_PCA9548_2_76_0,
  		 .num_ports = 4,
	},
	{
		.bus = LY8_PCA9548_77_5,
		{I2C_BOARD_INFO("pca9555", 0x24),
		 .platform_data = &ly8_gpio_24_2_data},
	},
	{
		.bus = LY8_PCA9548_77_6,
		{I2C_BOARD_INFO("pca9555", 0x23),
		 .platform_data = &ly8_gpio_23_2_data},
	},
	{
		.bus = LY8_PCA9548_77_7,
		{I2C_BOARD_INFO("pca9555", 0x23),
		 .platform_data = &ly8_gpio_23_3_data},
	},
	{
		.bus = LY8_PCA9548_77_7,
		{I2C_BOARD_INFO("pca9546", 0x76),
		 .platform_data = &ly8_data_pca9546_76,},
		 .has_port = 2, .port_base = 53,
  		 .num_ports = 2,
		 .port_bus = LY8_PCA9546_76_0,
	},

};

static struct i2c_client *i2c_clients_level1[ARRAY_SIZE(i2c_devices_level1)];
static struct i2c_client *i2c_clients_level2[ARRAY_SIZE(i2c_devices_level2)];
static struct i2c_client *i2c_clients_level3[ARRAY_SIZE(i2c_devices_level3)];

static struct i2c_client *add_i2c_client(int bus, struct i2c_board_info *board_info)
{
	struct i2c_adapter *adapter;
	struct i2c_client *client;

	adapter = i2c_get_adapter(bus);
	if (!adapter) {
		pr_err("could not get adapter %u\n", bus);
		return ERR_PTR(-ENODEV);
	}
	client = i2c_new_device(adapter, board_info);
	if (!client) {
		pr_err("could not add device\n");
		client = ERR_PTR(-ENODEV);
	}
	i2c_put_adapter(adapter);
	return client;
}

static int m21441_read(struct i2c_client *client, u8 reg)
{
	int value;

	value = i2c_smbus_read_byte_data(client, reg);
	if (value < 0) {
		dev_err(&client->dev, "Read to %d failed !\n", reg);
	}
	return value;
}

static int m21441_write(struct i2c_client *client, u8 reg, u8 value)
{
	int ret;

	ret = i2c_smbus_write_byte_data(client, reg, value);
	if (ret < 0) {
		dev_err(&client->dev,"Write to %d failed !\n", reg);
	}
	return ret;
}

static int m21441_init(struct i2c_client *client)
{
#define M21441_REG_PAGE_ADDRESS		0xFF
#define M21441_REG_MASTER_RESET		0xE0
#define M21441_REG_CHIP_ID              0xE1
#define M21441_REG_GEN_SW_CONFIG	0x01

#define M21441_REG_ASC_REG1_KR0	        0x10
#define M21441_REG_ASC_REG1_KR1	        0x11
#define M21441_REG_ASC_REG1_KR2	        0x12
#define M21441_REG_ASC_REG1_KR3	        0x13
#define M21441_REG_ASC_REG1_XFI0	0x14
#define M21441_REG_ASC_REG1_XFI1	0x15
#define M21441_REG_ASC_REG1_XFI2	0x16
#define M21441_REG_ASC_REG1_XFI3	0x17
#define M21441_REG_ASC_REG1_KR0r	0x18
#define M21441_REG_ASC_REG1_KR1r	0x19
#define M21441_REG_ASC_REG1_KR2r	0x1a
#define M21441_REG_ASC_REG1_KR3r	0x1b

#define M21441_REG_CONFIG_REG1_KR0	0x74
#define M21441_REG_CONFIG_REG1_KR1	0x79
#define M21441_REG_CONFIG_REG1_KR2	0x7e
#define M21441_REG_CONFIG_REG1_KR3	0x83
#define M21441_REG_CONFIG_REG1_XFI0	0x88
#define M21441_REG_CONFIG_REG1_XFI1	0x8d
#define M21441_REG_CONFIG_REG1_XFI2	0x92
#define M21441_REG_CONFIG_REG1_XFI3	0x97
#define M21441_REG_CONFIG_REG1_KR0r	0x9c
#define M21441_REG_CONFIG_REG1_KR1r	0xa1
#define M21441_REG_CONFIG_REG1_KR2r	0xa6
#define M21441_REG_CONFIG_REG1_KR3r	0xab

#define M21441_REG_CONFIG_REG3_XFI0	0x8a
#define M21441_REG_CONFIG_REG3_XFI1	0x8f
#define M21441_REG_CONFIG_REG3_XFI2	0x94
#define M21441_REG_CONFIG_REG3_XFI3	0x99

#define M21441_REG_CDR_REG1_KR0 	0x47
#define M21441_REG_CDR_REG1_KR1	        0x57
#define M21441_REG_CDR_REG1_KR2	        0x67
#define M21441_REG_CDR_REG1_KR3	        0x77
#define M21441_REG_CDR_REG1_XFI0	0x4b
#define M21441_REG_CDR_REG1_XFI1	0x5b
#define M21441_REG_CDR_REG1_XFI2	0x6b
#define M21441_REG_CDR_REG1_XFI3	0x7b
#define M21441_REG_CDR_REG1_KR0r	0x4f
#define M21441_REG_CDR_REG1_KR1r	0x5f
#define M21441_REG_CDR_REG1_KR2r	0x6f
#define M21441_REG_CDR_REG1_KR3r	0x7f

#define M21441_REG_PLL0_REG1            0x40
#define M21441_REG_PLL1_REG1            0x50
#define M21441_REG_PLL2_REG1            0x60
#define M21441_REG_PLL3_REG1            0x70

	static unsigned char out_signal_reg[12] = {
		M21441_REG_CONFIG_REG1_KR0,
		M21441_REG_CONFIG_REG1_KR1,
		M21441_REG_CONFIG_REG1_KR2,
		M21441_REG_CONFIG_REG1_KR3,
		M21441_REG_CONFIG_REG1_XFI0,
		M21441_REG_CONFIG_REG1_XFI1,
		M21441_REG_CONFIG_REG1_XFI2,
		M21441_REG_CONFIG_REG1_XFI3,
		M21441_REG_CONFIG_REG1_KR0r,
		M21441_REG_CONFIG_REG1_KR1r,
		M21441_REG_CONFIG_REG1_KR2r,
		M21441_REG_CONFIG_REG1_KR3r,
	};

	static unsigned char asc_val[12] = {
		0x06,
		0x0A,
		0x03,
		0x02,
		0x0B,
		0x07,
		0x05,
		0x09,
		0x01,
		0x00,
		0x08,
		0x04,
	};

	static unsigned char asc_reg[12] = {
		M21441_REG_ASC_REG1_KR0,
		M21441_REG_ASC_REG1_KR1,
		M21441_REG_ASC_REG1_KR2,
		M21441_REG_ASC_REG1_KR3,
		M21441_REG_ASC_REG1_XFI0,
		M21441_REG_ASC_REG1_XFI1,
		M21441_REG_ASC_REG1_XFI2,
		M21441_REG_ASC_REG1_XFI3,
		M21441_REG_ASC_REG1_KR0r,
		M21441_REG_ASC_REG1_KR1r,
		M21441_REG_ASC_REG1_KR2r,
		M21441_REG_ASC_REG1_KR3r
	};

	static unsigned char out_enable_reg[4] = {
		M21441_REG_CONFIG_REG3_XFI0,
		M21441_REG_CONFIG_REG3_XFI1,
		M21441_REG_CONFIG_REG3_XFI2,
		M21441_REG_CONFIG_REG3_XFI3,
	};

	static unsigned char cdr_reg[12] = {
		M21441_REG_CDR_REG1_KR0,
		M21441_REG_CDR_REG1_KR1,
		M21441_REG_CDR_REG1_KR2,
		M21441_REG_CDR_REG1_KR3,
		M21441_REG_CDR_REG1_XFI0,
		M21441_REG_CDR_REG1_XFI1,
		M21441_REG_CDR_REG1_XFI2,
		M21441_REG_CDR_REG1_XFI3,
		M21441_REG_CDR_REG1_KR0r,
		M21441_REG_CDR_REG1_KR1r,
		M21441_REG_CDR_REG1_KR2r,
		M21441_REG_CDR_REG1_KR3r,
	};

	u8 value;
	unsigned int index, ret = 0;

	/* Change to Page 0 */
	ret = m21441_write(client, M21441_REG_PAGE_ADDRESS, 0);
	if (ret < 0)
		return ret;

	/* Check Chip_id */
	value = m21441_read(client, M21441_REG_CHIP_ID);
	if (value == 0xE3) {
		dev_info(&client->dev, "Detected M21441 ID=0x%x\n", value);
	} else {
		dev_err(&client->dev, "Chip M2141 not detected: 0x%x\n", value);
		return value;
	}
	/* Software Reset On */
	ret = m21441_write(client, M21441_REG_MASTER_RESET, 0xaa);
	if (ret < 0)
		return ret;

	/* Software Reset Off */
	ret = m21441_write(client, M21441_REG_MASTER_RESET, 0x00);
	if (ret < 0)
		return ret;

	/* CDR scale and peak override */
	for (index = 0; index < 12; index++) {
		ret = m21441_write(client, cdr_reg[index], 0x60);
		if (ret < 0)
			return ret;
	}

	/* CDR Power Down */
	ret = m21441_write(client, M21441_REG_CDR_REG1_KR1, 0x80);
	if (ret < 0)
		return ret;

	ret = m21441_write(client, M21441_REG_CDR_REG1_XFI1, 0x80);
	if (ret < 0)
		return ret;

	ret = m21441_write(client, M21441_REG_CDR_REG1_KR3, 0x80);
	if (ret < 0)
		return ret;

	ret = m21441_write(client, M21441_REG_CDR_REG1_XFI3, 0x80);
	if (ret < 0)
		return ret;


	/* PLL Config */
	ret = m21441_write(client, M21441_REG_PLL1_REG1, 0x60);
	if (ret < 0)
		return ret;

	ret = m21441_write(client, M21441_REG_PLL3_REG1, 0x60);
	if (ret < 0)
		return ret;

	ret = m21441_write(client, M21441_REG_PLL0_REG1, 0x0);
	if (ret < 0)
		return ret;

	ret = m21441_write(client, M21441_REG_PLL2_REG1, 0x0);
	if (ret < 0)
		return ret;


	/* Change to Channel Mode */
	ret = m21441_write(client, M21441_REG_GEN_SW_CONFIG, 0x10);
	if (ret < 0)
		return ret;


	/* Active Software State */
	for (index = 0; index < 12; index++) {
		value = asc_val[index];
		ret = m21441_write(client, asc_reg[index], value);
		if (ret < 0)
			return ret;

		ret = m21441_write(client, asc_reg[index] + 10, value);
		if (ret < 0)
			return ret;
		ret = m21441_write(client, asc_reg[index] + 20, value);
		if (ret < 0)
			return ret;
	}

        /* Change to Page 1 */
	ret = m21441_write(client, M21441_REG_PAGE_ADDRESS, 1);
	if (ret < 0)
		return ret;

	/* Output channel enable */
	for (index = 0; index < 4; index++) {
		ret = m21441_write(client, out_enable_reg[index], 0x80);
		if (ret < 0)
			return ret;
	}
	/* Output channel signal*/
	for (index = 0; index <= 3; index++) {
		ret = m21441_write(client, out_signal_reg[index], 0x40);
		if (ret < 0)
			return ret;
	}

	for (index = 8; index < 12; index++) {
		ret = m21441_write(client, out_signal_reg[index], 0xa9);
		if (ret < 0)
			return ret;
	}

	dev_info(&client->dev, "M21441 Equalizer updated");
	return 0;
}

static int ly8_gpio_pins_init(struct ly8_gpio_pin_info *info, int base, int count)
{
	struct ly8_gpio_pin_info *p;
	int i;
	int ret;
	int num;

	for (i = 0; i < count; i++) {
		p = &info[i];
		num = base + i;

		if (p->nc) {
			continue;
		}

		ret = gpio_request(num, NULL);
		if (ret) {
			pr_err("request for gpio pin %s (%u) failed\n",
			       p->name, num);
			return ret;
		}

		if (p->direction)
			ret = gpio_direction_output(num, p->value);
		else
			ret = gpio_direction_input(num);
		if (ret) {
			pr_err("unable to set direction on gpio pin %s (%u)\n",
			       p->name, num);
			return ret;
		}
		ret = gpio_export(num, false);
		if (ret) {
			pr_err("unable to export gpio pin %s (%u)\n",
			       p->name, num);
			return ret;
		}
	}
	return 0;
}

#define SFP_LABEL_SIZE  8
static struct i2c_board_info *alloc_sfp_board_info(int port) {
	char *label;
	struct eeprom_platform_data *eeprom_data;
	struct at24_platform_data *at24_data;
	struct i2c_board_info *board_info;

	label = kzalloc(SFP_LABEL_SIZE, GFP_KERNEL);
	if (!label) {
		goto err_exit;
	}
	eeprom_data = kzalloc(sizeof(struct eeprom_platform_data), GFP_KERNEL);
	if (!eeprom_data) {
		goto err_exit_eeprom;
	}
	at24_data = kzalloc(sizeof(struct at24_platform_data), GFP_KERNEL);
	if (!at24_data) {
		goto err_exit_at24;
	}
	board_info = kzalloc(sizeof(struct i2c_board_info), GFP_KERNEL);
	if (!board_info) {
		goto err_exit_board;
	}

	snprintf(label, SFP_LABEL_SIZE, "port%u", port);
	eeprom_data->label = label;

	at24_data->byte_len = 512;
	at24_data->flags = AT24_FLAG_IRUGO;
	at24_data->page_size = 1;
	at24_data->eeprom_data = eeprom_data;

	strcpy(board_info->type, "24c04");
	board_info->addr = 0x50;
	board_info->platform_data = at24_data;

	return board_info;

err_exit_board:
	kfree(at24_data);
err_exit_at24:
	kfree(eeprom_data);
err_exit_eeprom:
	kfree(label);
err_exit:
	return NULL;
}

#define QSFP_LABEL_SIZE  8
static struct i2c_board_info *alloc_qsfp_board_info(int port) {
	char *label;
	struct eeprom_platform_data *eeprom_data;
	struct sff_8436_platform_data *sff8436_data;
	struct i2c_board_info *board_info;

	label = kzalloc(QSFP_LABEL_SIZE, GFP_KERNEL);
	if (!label) {
		goto err_exit;
	}
	eeprom_data = kzalloc(sizeof(struct eeprom_platform_data), GFP_KERNEL);
	if (!eeprom_data) {
		goto err_exit_eeprom;
	}
	sff8436_data = kzalloc(sizeof(struct sff_8436_platform_data), GFP_KERNEL);
	if (!sff8436_data) {
		goto err_exit_sff8436;
	}
	board_info = kzalloc(sizeof(struct i2c_board_info), GFP_KERNEL);
	if (!board_info) {
		goto err_exit_board;
	}

	snprintf(label, QSFP_LABEL_SIZE, "port%u", port);
	eeprom_data->label = label;

	sff8436_data->byte_len = 256;
	sff8436_data->flags = SFF_8436_FLAG_IRUGO;
	sff8436_data->page_size = 1;
	sff8436_data->eeprom_data = eeprom_data;

	strcpy(board_info->type, "sff8436");
	board_info->addr = 0x50;
	board_info->platform_data = sff8436_data;

	return board_info;

err_exit_board:
	kfree(sff8436_data);
err_exit_sff8436:
	kfree(eeprom_data);
err_exit_eeprom:
	kfree(label);
err_exit:
	return NULL;
}

static void free_sfp_board_info(struct i2c_board_info *board_info)
{
	struct at24_platform_data *at24_data = board_info->platform_data;
	struct eeprom_platform_data *eeprom_data = at24_data->eeprom_data;

	kfree(eeprom_data->label);
	kfree(eeprom_data);
	kfree(at24_data);
	kfree(board_info);
}

static void free_qsfp_board_info(struct i2c_board_info *board_info)
{
	struct sff_8436_platform_data *sff8436_data = board_info->platform_data;
	struct eeprom_platform_data *eeprom_data = sff8436_data->eeprom_data;

	kfree(eeprom_data->label);
	kfree(eeprom_data);
	kfree(sff8436_data);
	kfree(board_info);
}

static int ly8_alloc_sfp(int port, int bus, int num_ports) {
	struct i2c_board_info *b_info;
	struct i2c_client *c;
	int j;

	for (j = 0; j < num_ports; j++) {
		b_info = alloc_sfp_board_info(port + j);
		if (!b_info) {
			pr_err("could not allocate board info port: %d\n", port + j);
			return -1;
		}
		c = add_i2c_client(bus + j, b_info);
		if (!c) {
			free_sfp_board_info(b_info);
			pr_err("could not create i2c_client %s port: %d\n", b_info->type, port + j);
			return -1;
		}
		ports_info[port + j - 1].b = b_info;
		ports_info[port + j - 1].c = c;
	}
	return 0;
}

static int ly8_alloc_qsfp(int port, int bus, int num_ports) {
	struct i2c_board_info *b_info;
	struct i2c_client *c;
	int j;

	for (j = 0; j < num_ports; j++) {
		b_info = alloc_qsfp_board_info(port + j);
		if (!b_info) {
			pr_err("could not allocate board info port: %d\n", port + j);
			return -1;
		}
		c = add_i2c_client(bus + j, b_info);
		if (!c) {
			free_qsfp_board_info(b_info);
			pr_err("could not create i2c_client %s port: %d\n", b_info->type, port + j);
			return -1;
		}
		ports_info[port + j - 1].type = 1;
		ports_info[port + j - 1].b = b_info;
		ports_info[port + j - 1].c = c;
	}
	return 0;
}

static void ly8_free_data(void)
{
	int i;

	for(i = ly8_gpio_23_data.gpio_base;
	    i < ly8_gpio_23_data.gpio_base + 16; i++)
		gpio_free(i);

	for(i = ly8_gpio_24_data.gpio_base;
	    i < ly8_gpio_24_data.gpio_base + 16; i++)
		gpio_free(i);

	for(i = ly8_gpio_26_data.gpio_base;
	    i < ly8_gpio_26_data.gpio_base + 16; i++)
		gpio_free(i);

	for(i = ly8_gpio_23_2_data.gpio_base;
	    i < ly8_gpio_23_2_data.gpio_base + 16; i++)
		gpio_free(i);

	for(i = ly8_gpio_24_2_data.gpio_base;
	    i < ly8_gpio_24_2_data.gpio_base + 16; i++)
		gpio_free(i);

	for(i = ly8_gpio_23_3_data.gpio_base;
	    i < ly8_gpio_23_3_data.gpio_base + 16; i++)
		gpio_free(i);

	for(i = ly8_gpio_20_data.gpio_base;
	    i < ly8_gpio_20_data.gpio_base + 16; i++)
		gpio_free(i);

	for(i = ly8_gpio_sfp_1_8_data.gpio_base;
	    i < ly8_gpio_sfp_1_8_data.gpio_base + 32; i++)
		gpio_free(i);

	for(i = ly8_gpio_sfp_9_16_data.gpio_base;
	    i < ly8_gpio_sfp_9_16_data.gpio_base + 32; i++)
		gpio_free(i);

	for(i = ly8_gpio_sfp_17_24_data.gpio_base;
	    i < ly8_gpio_sfp_17_24_data.gpio_base + 32; i++)
		gpio_free(i);

	for(i = ly8_gpio_sfp_25_32_data.gpio_base;
	    i < ly8_gpio_sfp_25_32_data.gpio_base + 32; i++)
		gpio_free(i);

	for(i = ly8_gpio_sfp_33_40_data.gpio_base;
	    i < ly8_gpio_sfp_33_40_data.gpio_base + 32; i++)
		gpio_free(i);

	for(i = ly8_gpio_sfp_41_48_data.gpio_base;
	    i < ly8_gpio_sfp_41_48_data.gpio_base + 32; i++)
		gpio_free(i);

	for(i = 0; i < LY8_PORT_COUNT; i++) {
		if (ports_info[i].b) {
			if (ports_info[i].type == 1) {
				free_qsfp_board_info(ports_info[i].b);
			} else {
				free_sfp_board_info(ports_info[i].b);
			}
		}
		if (ports_info[i].c) {
			i2c_unregister_device(ports_info[i].c);
		}
	}

	for (i = 0; i < ARRAY_SIZE(i2c_devices_level3); i++)
		i2c_unregister_device(i2c_clients_level3[i]);

	for (i = 0; i < ARRAY_SIZE(i2c_devices_level2); i++)
		i2c_unregister_device(i2c_clients_level2[i]);

	for (i = 0; i < ARRAY_SIZE(i2c_devices_level1); i++)
		i2c_unregister_device(i2c_clients_level1[i]);
}

static int __init ly8_platform_init(void)
{
	struct i2c_adapter *adapter;
	int ret = -1;
	int i801_bus = -1;
	struct i2c_client *m21441_client = NULL;
	int i;

	/* Find the I801 bus */
	for (i = 0; i < 5; i++) {
		adapter = i2c_get_adapter(i);
		if (adapter) {
			if (!strncmp(adapter->name, SMB_I801_NAME, strlen(SMB_I801_NAME)))
				i801_bus = i;
			i2c_put_adapter(adapter);
		}
		if (i801_bus >= 0)
			break;
	}
	if (i801_bus < 0) {
		pr_err("Unable to find %s\n", SMB_I801_NAME);
		return -ENXIO;
	}

	/* Fill in the GPIO names */
	for (i = 0; i < PCA_9555_GPIO_COUNT; i++) {
		ly8_gpio_24_names[i] = ly8_gpio_24_info[i].name;
		ly8_gpio_23_names[i] = ly8_gpio_23_info[i].name;
		ly8_gpio_26_names[i] = ly8_gpio_26_info[i].name;
		ly8_gpio_24_2_names[i] = ly8_gpio_24_2_info[i].name;
		ly8_gpio_23_2_names[i] = ly8_gpio_23_2_info[i].name;
		ly8_gpio_23_3_names[i] = ly8_gpio_23_3_info[i].name;
		ly8_gpio_20_names[i] = ly8_gpio_20_info[i].name;
	}
	for (i = 0; i < PCA_9698_GPIO_COUNT; i++) {
		ly8_gpio_sfp_1_8_names[i] = ly8_gpio_sfp_1_8_info[i].name;
		ly8_gpio_sfp_9_16_names[i] = ly8_gpio_sfp_9_16_info[i].name;
		ly8_gpio_sfp_17_24_names[i] = ly8_gpio_sfp_17_24_info[i].name;
		ly8_gpio_sfp_25_32_names[i] = ly8_gpio_sfp_25_32_info[i].name;
		ly8_gpio_sfp_33_40_names[i] = ly8_gpio_sfp_33_40_info[i].name;
		ly8_gpio_sfp_41_48_names[i] = ly8_gpio_sfp_41_48_info[i].name;
	}

	/* Instantiate I2C devices */
	for (i = 0; i < ARRAY_SIZE(i2c_devices_level1); i++) {
		if (i2c_devices_level1[i].bus == LY8_I2C_I801_BUS)
			i2c_devices_level1[i].bus = i801_bus;
		i2c_clients_level1[i] = add_i2c_client(i2c_devices_level1[i].bus,
						       &i2c_devices_level1[i].info);
		if (IS_ERR(i2c_clients_level1[i])) {
			ret = PTR_ERR(i2c_clients_level1[i]);
			goto err_exit;
		}
	}
	for (i = 0; i < ARRAY_SIZE(i2c_devices_level2); i++) {
		i2c_clients_level2[i] = add_i2c_client(i2c_devices_level2[i].bus,
						       &i2c_devices_level2[i].info);
		if (IS_ERR(i2c_clients_level2[i])) {
			ret = PTR_ERR(i2c_clients_level2[i]);
			goto err_exit;
		}
		if (i2c_devices_level2[i].has_port == 1) {
			ly8_alloc_sfp(i2c_devices_level2[i].port_base,
				      i2c_devices_level2[i].port_bus,
				      i2c_devices_level2[i].num_ports);
		} else if (i2c_devices_level2[i].has_port == 2) {
			ly8_alloc_qsfp(i2c_devices_level2[i].port_base,
				       i2c_devices_level2[i].port_bus,
   				       i2c_devices_level2[i].num_ports);
		}
	}
	for (i = 0; i < ARRAY_SIZE(i2c_devices_level3); i++) {
		i2c_clients_level3[i] = add_i2c_client(i2c_devices_level3[i].bus,
						       &i2c_devices_level3[i].info);
		if (IS_ERR(i2c_clients_level3[i])) {
			ret = PTR_ERR(i2c_clients_level3[i]);
			goto err_exit;
		}
		if (i2c_devices_level3[i].info.addr == 0x21)
			m21441_client = i2c_clients_level3[i];
	}
	if (m21441_client != NULL) {
		ret = m21441_init(m21441_client);
		if (ret < 0)
			pr_err("Initialization of M21441 failed\n");
	} else {
		pr_err("M21441 device not found\n");
	}

	/* Instantiate GPIOs */
	ly8_gpio_pins_init(ly8_gpio_24_info, ly8_gpio_24_data.gpio_base, PCA_9555_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_23_info, ly8_gpio_23_data.gpio_base, PCA_9555_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_26_info, ly8_gpio_26_data.gpio_base, PCA_9555_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_24_2_info, ly8_gpio_24_2_data.gpio_base, PCA_9555_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_23_2_info, ly8_gpio_23_2_data.gpio_base, PCA_9555_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_23_3_info, ly8_gpio_23_3_data.gpio_base, PCA_9555_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_20_info, ly8_gpio_20_data.gpio_base, PCA_9555_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_sfp_1_8_info, ly8_gpio_sfp_1_8_data.gpio_base, PCA_9698_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_sfp_9_16_info, ly8_gpio_sfp_9_16_data.gpio_base, PCA_9698_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_sfp_17_24_info, ly8_gpio_sfp_17_24_data.gpio_base, PCA_9698_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_sfp_25_32_info, ly8_gpio_sfp_25_32_data.gpio_base, PCA_9698_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_sfp_33_40_info, ly8_gpio_sfp_33_40_data.gpio_base, PCA_9698_GPIO_COUNT);
	ly8_gpio_pins_init(ly8_gpio_sfp_41_48_info, ly8_gpio_sfp_41_48_data.gpio_base, PCA_9698_GPIO_COUNT);

	ret = platform_driver_register(&ly8_platform_driver);
	if (ret)
		goto err_exit;

	pr_info(DRIVER_NAME": version "DRIVER_VERSION" successfully loaded\n");
	return 0;

err_exit:
	ly8_free_data();
	return ret;
}

static void __exit ly8_platform_exit(void)
{
	ly8_free_data();
	platform_driver_unregister(&ly8_platform_driver);
	pr_info(DRIVER_NAME": version "DRIVER_VERSION" unloaded\n");
}

static int ly8_platform_probe(struct platform_device *dev)
{
	return 0;
}

static int ly8_platform_remove(struct platform_device *dev)
{
	return 0;
}

static struct platform_driver ly8_platform_driver = {
	.driver = {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
	},
	.probe = ly8_platform_probe,
	.remove = ly8_platform_remove,
};

module_init(ly8_platform_init);
module_exit(ly8_platform_exit);

MODULE_AUTHOR("Puneet Shenoy (puneet@cumulusnetworks.com)");
MODULE_DESCRIPTION("Quanta LY8 Platform Support");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
