/*
 * quanta_ly6_platform.c - Quanta LY6 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_ly6_rangeley_platform"
#define DRIVER_VERSION	"1.1"
#define SMB_I801_NAME "SMBus I801 adapter"
#define LY6_I2C_I801_BUS 0
#define PCA_9555_GPIO_COUNT 16
#define PCA_9698_GPIO_COUNT 40
#define LY6_PORT_COUNT 32

static struct platform_driver ly6_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 ly6_device_info {
	int bus;
	struct i2c_board_info info;
	int has_port;  /* 1 for SFP, 2 for QSFP */
	int port_base;
	int port_bus;
};

struct ly6_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[LY6_PORT_COUNT];

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


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

/* I2C I801 -> PCA9546(0x71) */
ly6_pca9546(71, 10);
/* I2C I801 -> PCA9548_1(0x77) */
ly6_pca9548(77, 14)
/* I2C I801 -> PCA9548_1(0x77) -> PCA9548_2 (0x73) */
ly6_pca9548(73, 22)
/* I2C I801 -> PCA9548_1(0x77) -> PCA9548_2 (0x74) */
ly6_pca9548(74, 30)
/* I2C I801 -> PCA9548_1(0x77) -> PCA9548_3 (0x75) */
ly6_pca9548(75, 38)
/* I2C I801 -> PCA9548_1(0x77) -> PCA9548_3 (0x76) */
ly6_pca9548(76, 46)
/* I2C I801 -> PCA9546(0x71) - > PCA9546(0x72) */
ly6_pca9546(72, 54);

/* GPIOS */
#define LY6_GPIO_BASE 100
#define LY6_GPIO_20_BASE LY6_GPIO_BASE
#define LY6_GPIO_24_BASE (LY6_GPIO_20_BASE + PCA_9555_GPIO_COUNT)
#define LY6_GPIO_23_BASE (LY6_GPIO_24_BASE + PCA_9555_GPIO_COUNT)
#define LY6_GPIO_26_BASE (LY6_GPIO_23_BASE + PCA_9555_GPIO_COUNT)

static struct ly6_gpio_pin_info ly6_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 *ly6_gpio_20_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly6_gpio_20_data = {
	.gpio_base = LY6_GPIO_20_BASE,
	.names = ly6_gpio_20_names,
};

static struct ly6_gpio_pin_info ly6_gpio_24_info[PCA_9555_GPIO_COUNT] = {
	{.name = "cpld2_int",},
	{.name = "cpld3_int",},
	{.name = "9555_4_int",},
	{.name = "bcm5461s_int",},

	{.name = "fan1_present",}, /* 0 is present */
	{.name = "fan2_present",},
	{.name = "fan3_present",},
	{.name = "fan4_present",},

	{.name = "fan1_f2b",}, /* 1 is F2B, 0 is B2F */
	{.name = "fan2_f2b",},
	{.name = "fan3_f2b",},
	{.name = "fan4_f2b",},

	{.name = "fan1_fail_led", .direction = 1, .value = 0,}, /* 1 is fail */
	{.name = "fan2_fail_led", .direction = 1, .value = 0,},
	{.name = "fan3_fail_led", .direction = 1, .value = 0,},
	{.name = "fan4_fail_led", .direction = 1, .value = 0,},
};

static const char *ly6_gpio_24_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly6_gpio_24_data = {
	.gpio_base = LY6_GPIO_24_BASE,
	.names = ly6_gpio_24_names,
};

static struct ly6_gpio_pin_info ly6_gpio_23_info[PCA_9555_GPIO_COUNT] = {
	{.name = "fan0_board",},
	{.name = "fan1_board",},
	{.name = "fan2_board",},
	{.name = "fan3_board",},

	{.name = "qsfp_power_good",},
	{.name = "nc_1",},
	{.name = "nc_2",},
	{.name = "mac_rst", .direction = 1, .value = 1,},

	{.name = "qsfp_pwr_enable", .direction = 1, .value = 1,},
	{.name = "usb_reset",       .direction = 1, .value = 1,},
	{.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 *ly6_gpio_23_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly6_gpio_23_data = {
	.gpio_base = LY6_GPIO_23_BASE,
	.names = ly6_gpio_23_names,
};

static struct ly6_gpio_pin_info ly6_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",},
	{.name = "psu_pwr2_reset",},
	{.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 *ly6_gpio_26_names[PCA_9555_GPIO_COUNT];
static struct pca953x_platform_data ly6_gpio_26_data = {
	.gpio_base = LY6_GPIO_26_BASE,
	.names = ly6_gpio_26_names,
};

struct quanta_ly6_rangeley_platform_data {
	int idx;
};

static struct quanta_ly6_rangeley_platform_data cpld_1 = {
	.idx = 1,
};

static struct quanta_ly6_rangeley_platform_data cpld_2 = {
	.idx = 2,
};

/* I2C Device Map */
static struct ly6_device_info i2c_devices_level1[] = {
	{
		.bus = LY6_I2C_I801_BUS,       /* SPD DIMM EEPROM */
		{I2C_BOARD_INFO("spd", 0x52),
		 .platform_data = &spd1_52_at24,},
	},
	{
		.bus = LY6_I2C_I801_BUS,
		{I2C_BOARD_INFO("pca9546", 0x71),
		 .platform_data = &ly6_data_pca9546_71,},
	},
	{
		.bus = LY6_I2C_I801_BUS,     /* Sensors Chip */
		{I2C_BOARD_INFO("CY8C3245R1", 0x4e),},
	},
	{
		.bus = LY6_I2C_I801_BUS,
		{I2C_BOARD_INFO("pca9548", 0x77),
		 .platform_data = &ly6_data_pca9548_77,},
	},
	{
		.bus = LY6_I2C_I801_BUS,
		{I2C_BOARD_INFO("pca9546", 0x72),
		 .platform_data = &ly6_data_pca9546_72,},
	},
};

static struct ly6_device_info i2c_devices_level2[] = {
	/* Following devices are on CPU Board */
	{
		.bus = LY6_PCA9546_71_0,
		{I2C_BOARD_INFO("pca9555", 0x20),
		.platform_data = &ly6_gpio_20_data},
	},
	{
		.bus = LY6_PCA9546_71_1,
		{I2C_BOARD_INFO("24c02", 0x53),
		 .platform_data = &cpu_53_at24,},
	},
	{
		.bus = LY6_PCA9548_77_0,
		{I2C_BOARD_INFO("pca9548", 0x73),
		 .platform_data = &ly6_data_pca9548_73,},
		 .has_port = 2, .port_base = 1,
		 .port_bus = LY6_PCA9548_73_0,
	},
	{
		.bus = LY6_PCA9548_77_1,
		{I2C_BOARD_INFO("pca9548", 0x74),
		 .platform_data = &ly6_data_pca9548_74,},
		 .has_port = 2, .port_base = 9,
		 .port_bus = LY6_PCA9548_74_0,
	},
	{
		.bus = LY6_PCA9548_77_2,
		{I2C_BOARD_INFO("pca9548", 0x75),
		 .platform_data = &ly6_data_pca9548_75,},
		 .has_port = 2, .port_base = 17,
		 .port_bus = LY6_PCA9548_75_0,
	},
	{
		.bus = LY6_PCA9548_77_3,
		{I2C_BOARD_INFO("pca9548", 0x76),
		 .platform_data = &ly6_data_pca9548_76,},
		 .has_port = 2, .port_base = 25,
		 .port_bus = LY6_PCA9548_76_0,
	},
	{
	        .bus = LY6_PCA9548_77_4,
		{I2C_BOARD_INFO("ly6_rangeley_cpld", 0x3a),
		 .platform_data = &cpld_1},
	},
	{
	        .bus = LY6_PCA9548_77_5,
		{I2C_BOARD_INFO("ly6_rangeley_cpld", 0x3b),
		 .platform_data = &cpld_2},
	},
	{
		.bus = LY6_PCA9548_77_6,
		{I2C_BOARD_INFO("pca9555", 0x24),
		 .platform_data = &ly6_gpio_24_data},
	},
	{
		.bus = LY6_PCA9548_77_7,
		{I2C_BOARD_INFO("pca9555", 0x23),
		 .platform_data = &ly6_gpio_23_data},
	},
	{
		.bus = LY6_PCA9546_72_0, /* PSU1 PMBUS */
		{I2C_BOARD_INFO("ps2471", 0x58),},
	},
	{
		.bus = LY6_PCA9546_72_1, /* PSU2 PMBUS */
		{I2C_BOARD_INFO("ps2471", 0x59),},
	},
	{
		.bus = LY6_PCA9546_72_2,
		{I2C_BOARD_INFO("pca9555", 0x26),
		.platform_data = &ly6_gpio_26_data},
	},
	{
		.bus = LY6_PCA9546_72_3,   /* Board EEPROM  */
		{I2C_BOARD_INFO("24c02", 0x54),
		 .platform_data = &board_54_at24,},
	},

};

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 *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 ly6_gpio_pins_init(struct ly6_gpio_pin_info *info, int base, int count)
{
	struct ly6_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 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_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 ly6_alloc_qsfp(int port, int bus) {
	struct i2c_board_info *b_info;
	struct i2c_client *c;
	int j;

	for (j = 0; j < 8; 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 ly6_free_data(void)
{
	int i;

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

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

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

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

	for(i = 0; i < LY6_PORT_COUNT; i++) {
		if (ports_info[i].b) {
			free_qsfp_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_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 ly6_platform_init(void)
{
	struct i2c_adapter *adapter;
	int ret = -1;
	int i801_bus = -1;
	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++) {
		ly6_gpio_20_names[i] = ly6_gpio_20_info[i].name;
		ly6_gpio_24_names[i] = ly6_gpio_24_info[i].name;
		ly6_gpio_23_names[i] = ly6_gpio_23_info[i].name;
		ly6_gpio_26_names[i] = ly6_gpio_26_info[i].name;
	}

	/* Instantiate I2C devices */
	for (i = 0; i < ARRAY_SIZE(i2c_devices_level1); i++) {
		if (i2c_devices_level1[i].bus == LY6_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 == 2) {
			ly6_alloc_qsfp(i2c_devices_level2[i].port_base,
				       i2c_devices_level2[i].port_bus);
		}

	}

	/* Instantiate GPIOs */
	ly6_gpio_pins_init(ly6_gpio_20_info, ly6_gpio_20_data.gpio_base, PCA_9555_GPIO_COUNT);
	ly6_gpio_pins_init(ly6_gpio_24_info, ly6_gpio_24_data.gpio_base, PCA_9555_GPIO_COUNT);
	ly6_gpio_pins_init(ly6_gpio_23_info, ly6_gpio_23_data.gpio_base, PCA_9555_GPIO_COUNT);
	ly6_gpio_pins_init(ly6_gpio_26_info, ly6_gpio_26_data.gpio_base, PCA_9555_GPIO_COUNT);

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

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

err_exit:
	ly6_free_data();
	return ret;
}

static void __exit ly6_platform_exit(void)
{
	ly6_free_data();
	platform_driver_unregister(&ly6_platform_driver);
	pr_info(DRIVER_NAME": version "DRIVER_VERSION" unloaded\n");
}

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

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

static struct platform_driver ly6_platform_driver = {
	.driver = {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
	},
	.probe = ly6_platform_probe,
	.remove = ly6_platform_remove,
};

module_init(ly6_platform_init);
module_exit(ly6_platform_exit);

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