/*
 * Lenovo NE2580 platform driver
 *
 * Copyright (C) 2019 Cumulus Networks, Inc.  All rights reserved.
 * Author: David Yen <dhyen@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.
 *
 */

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

#include "platform_defs.h"
#include "lenovo_ne2580.h"

#define DRIVER_NAME	NE2580_PLATFORM_NAME
#define DRIVER_VERSION	"1.0"

/*
 * The platform has one i801 adapter.
 *
 * The i801 is connected to the following devices on the CPU board:
 *
 *    bios backup eeprom (0x56)
 *    onie eeprom (0x57)
 *    tmp75 (0x48)
 *
 * The i801 is connected to the following devices on the switch board:
 *
 *    cpld (0x77)
 *    vpd eeprom (0x55)
 *    swconfig eeprom (0x54)
 *    pca9543 2-channel switch (0x73)
 *	 0 psu1 (0x5a)
 *	   psu2 (0x5b)
 *	   ucd90160 (0x34)
 *	 1 tmp75 (0x4a)
 *	   tmp75 (0x4d)
 *	   tmp75 (0x4e)
 *    pca9548 8-channel mux (0x70)
 *	 0 pca9548 8-channel mux (0x72): swp49-swp56
 *	   pca9555 #1 16-channel gpio expander (0x20)
 *	   pca9555 #2 16-channel gpio expander (0x20)
 *	   pca9555 #3 16-channel gpio expander (0x20)
 *	 1 pca9548 8-channel mux (0x72): swp01-swp08
 *	   pca9555 #1 16-channel gpio expander (0x20)
 *	   pca9555 #2 16-channel gpio expander (0x20)
 *	   pca9555 #3 16-channel gpio expander (0x20)
 *	 2 pca9548 8-channel mux (0x72): swp09-swp16
 *	   pca9555 #1 16-channel gpio expander (0x20)
 *	   pca9555 #2 16-channel gpio expander (0x20)
 *	   pca9555 #3 16-channel gpio expander (0x20)
 *	 3 pca9548 8-channel mux (0x72): swp17-swp24
 *	   pca9555 #1 16-channel gpio expander (0x20)
 *	   pca9555 #2 16-channel gpio expander (0x20)
 *	   pca9555 #3 16-channel gpio expander (0x20)
 *	 4 pca9548 8-channel mux (0x72): swp25-swp32
 *	   pca9555 #1 16-channel gpio expander (0x20)
 *	   pca9555 #2 16-channel gpio expander (0x20)
 *	   pca9555 #3 16-channel gpio expander (0x20)
 *	 5 pca9548 8-channel mux (0x72): swp33-swp40
 *	   pca9555 #1 16-channel gpio expander (0x20)
 *	   pca9555 #2 16-channel gpio expander (0x20)
 *	   pca9555 #3 16-channel gpio expander (0x20)
 *	 6 pca9548 8-channel mux (0x72): swp41-swp48
 *	   pca9555 #1 16-channel gpio expander (0x20)
 *	   pca9555 #2 16-channel gpio expander (0x20)
 *	   pca9555 #3 16-channel gpio expander (0x20)
 *
 */

enum {
	I2C_I801_BUS = -1,

	/* 0x73 pca9543 switch */
	I2C_MUX1_BUS0 = 10,
	I2C_MUX1_BUS1,

	/* 0x70 pca9548 mux */
	I2C_MUX2_BUS0,
	I2C_MUX2_BUS1,
	I2C_MUX2_BUS2,
	I2C_MUX2_BUS3,
	I2C_MUX2_BUS4,
	I2C_MUX2_BUS5,
	I2C_MUX2_BUS6,
	I2C_MUX2_BUS7,

	/* 0x72 pca9548 mux */
	I2C_MUX3_BUS0 = 69, /* ports 49-56 */
	I2C_MUX3_BUS1,
	I2C_MUX3_BUS2,
	I2C_MUX3_BUS3,
	I2C_MUX3_BUS4,
	I2C_MUX3_BUS5,
	I2C_MUX3_BUS6,
	I2C_MUX3_BUS7,

	/* 0x72 pca9548 mux */
	I2C_MUX4_BUS0 = 21, /* ports 1-8... */
	I2C_MUX4_BUS1,
	I2C_MUX4_BUS2,
	I2C_MUX4_BUS3,
	I2C_MUX4_BUS4,
	I2C_MUX4_BUS5,
	I2C_MUX4_BUS6,
	I2C_MUX4_BUS7,

	/* 0x72 pca9548 mux */
	I2C_MUX5_BUS0,
	I2C_MUX5_BUS1,
	I2C_MUX5_BUS2,
	I2C_MUX5_BUS3,
	I2C_MUX5_BUS4,
	I2C_MUX5_BUS5,
	I2C_MUX5_BUS6,
	I2C_MUX5_BUS7,

	/* 0x72 pca9548 mux */
	I2C_MUX6_BUS0,
	I2C_MUX6_BUS1,
	I2C_MUX6_BUS2,
	I2C_MUX6_BUS3,
	I2C_MUX6_BUS4,
	I2C_MUX6_BUS5,
	I2C_MUX6_BUS6,
	I2C_MUX6_BUS7,

	/* 0x72 pca9548 mux */
	I2C_MUX7_BUS0,
	I2C_MUX7_BUS1,
	I2C_MUX7_BUS2,
	I2C_MUX7_BUS3,
	I2C_MUX7_BUS4,
	I2C_MUX7_BUS5,
	I2C_MUX7_BUS6,
	I2C_MUX7_BUS7,

	/* 0x72 pca9548 mux */
	I2C_MUX8_BUS0,
	I2C_MUX8_BUS1,
	I2C_MUX8_BUS2,
	I2C_MUX8_BUS3,
	I2C_MUX8_BUS4,
	I2C_MUX8_BUS5,
	I2C_MUX8_BUS6,
	I2C_MUX8_BUS7,

	/* 0x72 pca9548 mux */
	I2C_MUX9_BUS0,
	I2C_MUX9_BUS1,
	I2C_MUX9_BUS2,
	I2C_MUX9_BUS3,
	I2C_MUX9_BUS4,
	I2C_MUX9_BUS5,
	I2C_MUX9_BUS6,
	I2C_MUX9_BUS7,
};

/*
 * The list of i2c devices and their bus connections for this platform.
 *
 * First we construct the necessary data struction for each device,
 * using the method specific to the device type.  Then we put them
 * all together in a big table (see i2c_devices below).
 *
 * For muxes, we specify the starting bus number for the block of ports,
 * using the magic mk_pca954*() macros.
 *
 * For eeproms, including ones in the qsfp+ transceivers,
 * we specify the label, i2c address, size, and some flags,
 * all done in mk*_eeprom() macros.  The label is the string
 * that ends up in /sys/class/eeprom_dev/eepromN/label,
 * which we use to identify them at user level.
 *
 * See the comment below for gpio.
 */

mk_pca9543(mux1, I2C_MUX1_BUS0, 1)
mk_pca9548(mux2, I2C_MUX2_BUS0, 1)
mk_pca9548(mux3, I2C_MUX3_BUS0, 1)
mk_pca9548(mux4, I2C_MUX4_BUS0, 1)
mk_pca9548(mux5, I2C_MUX5_BUS0, 1)
mk_pca9548(mux6, I2C_MUX6_BUS0, 1)
mk_pca9548(mux7, I2C_MUX7_BUS0, 1)
mk_pca9548(mux8, I2C_MUX8_BUS0, 1)
mk_pca9548(mux9, I2C_MUX9_BUS0, 1)

mk_eeprom(swconfig, 54, 512, AT24_FLAG_IRUGO | AT24_FLAG_ADDR16)
mk_eeprom(vpd,	    55, 512, AT24_FLAG_IRUGO | AT24_FLAG_ADDR16)
mk_eeprom(bios,	    56, 512, AT24_FLAG_IRUGO | AT24_FLAG_ADDR16)
mk_eeprom(board,    57, 512, AT24_FLAG_IRUGO | AT24_FLAG_ADDR16)

mk_eeprom(psu1,	    52, 256, AT24_FLAG_IRUGO)
mk_eeprom(psu2,	    53, 256, AT24_FLAG_IRUGO)

mk_port_eeprom(port1,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port2,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port3,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port4,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port5,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port6,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port7,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port8,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port9,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port10, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port11, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port12, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port13, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port14, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port15, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port16, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port17, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port18, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port19, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port20, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port21, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port22, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port23, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port24, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port25, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port26, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port27, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port28, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port29, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port30, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port31, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port32, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port33, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port34, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port35, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port36, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port37, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port38, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port39, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port40, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port41, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port42, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port43, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port44, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port45, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port46, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port47, 50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port48, 50, 512, AT24_FLAG_IRUGO)

mk_qsfp_port_eeprom(port49, 50, 256, SFF_8436_FLAG_IRUGO)
mk_qsfp_port_eeprom(port50, 50, 256, SFF_8436_FLAG_IRUGO)
mk_qsfp_port_eeprom(port51, 50, 256, SFF_8436_FLAG_IRUGO)
mk_qsfp_port_eeprom(port52, 50, 256, SFF_8436_FLAG_IRUGO)
mk_qsfp_port_eeprom(port53, 50, 256, SFF_8436_FLAG_IRUGO)
mk_qsfp_port_eeprom(port54, 50, 256, SFF_8436_FLAG_IRUGO)
mk_qsfp_port_eeprom(port55, 50, 256, SFF_8436_FLAG_IRUGO)
mk_qsfp_port_eeprom(port56, 50, 256, SFF_8436_FLAG_IRUGO)

/* Some useful macros for defining gpios */

struct gpio_pin {
	int num;
	int pin;
	const char *name;
	bool output;
	bool active_low;
	bool value;		/* only for output pins */
	bool allocated;
};

#define mk_gpio_pins(_name) \
	static struct gpio_pin _name##_pins[]
#define mk_gpio_pin(_base, _pin, _name, _output, _active_low, _value)	\
	{ \
		.num = (_base + _pin), \
		.pin = (_pin), \
		.name = #_name,	\
		.output = (_output), \
		.active_low = (_active_low), \
		.value = (_value), \
	}
#define mk_gpio_platform_data(_name, _base, _numpins) \
	static char const *_name##_pinnames[_numpins]; \
	static struct pca953x_platform_data _name##_platform_data = { \
		.gpio_base = (_base), \
		.names = _name##_pinnames, \
	}

/* gpio definitions */

/* --- mux channel 0 --- */
mk_gpio_pins(gpio020) = {
	mk_gpio_pin(1,	0, port49_modsel,  1, 1, 0),
	mk_gpio_pin(1,	1, port50_modsel,  1, 1, 0),
	mk_gpio_pin(1,	2, port51_modsel,  1, 1, 0),
	mk_gpio_pin(1,	3, port52_modsel,  1, 1, 0),
	mk_gpio_pin(1,	4, port53_modsel,  1, 1, 0),
	mk_gpio_pin(1,	5, port54_modsel,  1, 1, 0),
	mk_gpio_pin(1,	6, port55_modsel,  1, 1, 0),
	mk_gpio_pin(1,	7, port56_modsel,  1, 1, 0),
	mk_gpio_pin(1,	8, port49_reset,   1, 1, 1),
	mk_gpio_pin(1,	9, port50_reset,   1, 1, 1),
	mk_gpio_pin(1, 10, port51_reset,   1, 1, 1),
	mk_gpio_pin(1, 11, port52_reset,   1, 1, 1),
	mk_gpio_pin(1, 12, port53_reset,   1, 1, 1),
	mk_gpio_pin(1, 13, port54_reset,   1, 1, 1),
	mk_gpio_pin(1, 14, port55_reset,   1, 1, 1),
	mk_gpio_pin(1, 15, port56_reset,   1, 1, 1),
};

mk_gpio_pins(gpio021) = {
	mk_gpio_pin(17,	 0, port49_lpmode,  1, 0, 0),
	mk_gpio_pin(17,	 1, port50_lpmode,  1, 0, 0),
	mk_gpio_pin(17,	 2, port51_lpmode,  1, 0, 0),
	mk_gpio_pin(17,	 3, port52_lpmode,  1, 0, 0),
	mk_gpio_pin(17,	 4, port53_lpmode,  1, 0, 0),
	mk_gpio_pin(17,	 5, port54_lpmode,  1, 0, 0),
	mk_gpio_pin(17,	 6, port55_lpmode,  1, 0, 0),
	mk_gpio_pin(17,	 7, port56_lpmode,  1, 0, 0),
};

mk_gpio_pins(gpio022) = {
	mk_gpio_pin(33,	 0, port49_rxlos,   0, 0, 0),
	mk_gpio_pin(33,	 1, port50_rxlos,   0, 0, 0),
	mk_gpio_pin(33,	 2, port51_rxlos,   0, 0, 0),
	mk_gpio_pin(33,	 3, port52_rxlos,   0, 0, 0),
	mk_gpio_pin(33,	 4, port53_rxlos,   0, 0, 0),
	mk_gpio_pin(33,	 5, port54_rxlos,   0, 0, 0),
	mk_gpio_pin(33,	 6, port55_rxlos,   0, 0, 0),
	mk_gpio_pin(33,	 7, port56_rxlos,   0, 0, 0),
	mk_gpio_pin(33,	 8, port49_present, 0, 1, 0),
	mk_gpio_pin(33,	 9, port50_present, 0, 1, 0),
	mk_gpio_pin(33, 10, port51_present, 0, 1, 0),
	mk_gpio_pin(33, 11, port52_present, 0, 1, 0),
	mk_gpio_pin(33, 12, port53_present, 0, 1, 0),
	mk_gpio_pin(33, 13, port54_present, 0, 1, 0),
	mk_gpio_pin(33, 14, port55_present, 0, 1, 0),
	mk_gpio_pin(33, 15, port56_present, 0, 1, 0),
};

/* --- mux channel 1 --- */
mk_gpio_pins(gpio120) = {
	mk_gpio_pin(101,  0, port1_tx_fault,	0, 0, 0),
	mk_gpio_pin(101,  1, port2_tx_fault,	0, 0, 0),
	mk_gpio_pin(101,  2, port3_tx_fault,	0, 0, 0),
	mk_gpio_pin(101,  3, port4_tx_fault,	0, 0, 0),
	mk_gpio_pin(101,  4, port1_present,	0, 1, 0),
	mk_gpio_pin(101,  5, port2_present,	0, 1, 0),
	mk_gpio_pin(101,  6, port3_present,	0, 1, 0),
	mk_gpio_pin(101,  7, port4_present,	0, 1, 0),
	mk_gpio_pin(101,  8, port1_tx_disable,	1, 0, 0),
	mk_gpio_pin(101,  9, port2_tx_disable,	1, 0, 0),
	mk_gpio_pin(101, 10, port3_tx_disable,	1, 0, 0),
	mk_gpio_pin(101, 11, port4_tx_disable,	1, 0, 0),
	mk_gpio_pin(101, 12, port1_rx_los,	0, 0, 0),
	mk_gpio_pin(101, 13, port2_rx_los,	0, 0, 0),
	mk_gpio_pin(101, 14, port3_rx_los,	0, 0, 0),
	mk_gpio_pin(101, 15, port4_rx_los,	0, 0, 0),
};

mk_gpio_pins(gpio121) = {
	mk_gpio_pin(117,  0, port5_tx_fault,	0, 0, 0),
	mk_gpio_pin(117,  1, port6_tx_fault,	0, 0, 0),
	mk_gpio_pin(117,  2, port7_tx_fault,	0, 0, 0),
	mk_gpio_pin(117,  3, port8_tx_fault,	0, 0, 0),
	mk_gpio_pin(117,  4, port5_present,	0, 1, 0),
	mk_gpio_pin(117,  5, port6_present,	0, 1, 0),
	mk_gpio_pin(117,  6, port7_present,	0, 1, 0),
	mk_gpio_pin(117,  7, port8_present,	0, 1, 0),
	mk_gpio_pin(117,  8, port5_tx_disable,	1, 0, 0),
	mk_gpio_pin(117,  9, port6_tx_disable,	1, 0, 0),
	mk_gpio_pin(117, 10, port7_tx_disable,	1, 0, 0),
	mk_gpio_pin(117, 11, port8_tx_disable,	1, 0, 0),
	mk_gpio_pin(117, 12, port5_rx_los,	0, 0, 0),
	mk_gpio_pin(117, 13, port6_rx_los,	0, 0, 0),
	mk_gpio_pin(117, 14, port7_rx_los,	0, 0, 0),
	mk_gpio_pin(117, 15, port8_rx_los,	0, 0, 0),
};

mk_gpio_pins(gpio122) = {
	mk_gpio_pin(133,  0, port1_rs0,		1, 0, 0),
	mk_gpio_pin(133,  1, port1_rs1,		1, 0, 0),
	mk_gpio_pin(133,  2, port2_rs0,		1, 0, 0),
	mk_gpio_pin(133,  3, port2_rs1,		1, 0, 0),
	mk_gpio_pin(133,  4, port3_rs0,		1, 0, 0),
	mk_gpio_pin(133,  5, port3_rs1,		1, 0, 0),
	mk_gpio_pin(133,  6, port4_rs0,		1, 0, 0),
	mk_gpio_pin(133,  7, port4_rs1,		1, 0, 0),
	mk_gpio_pin(133,  8, port5_rs0,		1, 0, 0),
	mk_gpio_pin(133,  9, port5_rs1,		1, 0, 0),
	mk_gpio_pin(133, 10, port6_rs0,		1, 0, 0),
	mk_gpio_pin(133, 11, port6_rs1,		1, 0, 0),
	mk_gpio_pin(133, 12, port7_rs0,		1, 0, 0),
	mk_gpio_pin(133, 13, port7_rs1,		1, 0, 0),
	mk_gpio_pin(133, 14, port8_rs0,		1, 0, 0),
	mk_gpio_pin(133, 15, port8_rs1,		1, 0, 0),
};

/* --- mux channel 2 --- */
mk_gpio_pins(gpio220) = {
	mk_gpio_pin(201,  0, port9_tx_fault,	0, 0, 0),
	mk_gpio_pin(201,  1, port10_tx_fault,	 0, 0, 0),
	mk_gpio_pin(201,  2, port11_tx_fault,	 0, 0, 0),
	mk_gpio_pin(201,  3, port12_tx_fault,	 0, 0, 0),
	mk_gpio_pin(201,  4, port9_present,	0, 1, 0),
	mk_gpio_pin(201,  5, port10_present,	 0, 1, 0),
	mk_gpio_pin(201,  6, port11_present,	 0, 1, 0),
	mk_gpio_pin(201,  7, port12_present,	 0, 1, 0),
	mk_gpio_pin(201,  8, port9_tx_disable,	1, 0, 0),
	mk_gpio_pin(201,  9, port10_tx_disable,	 1, 0, 0),
	mk_gpio_pin(201, 10, port11_tx_disable,	 1, 0, 0),
	mk_gpio_pin(201, 11, port12_tx_disable,	 1, 0, 0),
	mk_gpio_pin(201, 12, port9_rx_los,	0, 0, 0),
	mk_gpio_pin(201, 13, port10_rx_los,	 0, 0, 0),
	mk_gpio_pin(201, 14, port11_rx_los,	 0, 0, 0),
	mk_gpio_pin(201, 15, port12_rx_los,	 0, 0, 0),
};

mk_gpio_pins(gpio221) = {
	mk_gpio_pin(217,  0, port13_tx_fault,	 0, 0, 0),
	mk_gpio_pin(217,  1, port14_tx_fault,	 0, 0, 0),
	mk_gpio_pin(217,  2, port15_tx_fault,	 0, 0, 0),
	mk_gpio_pin(217,  3, port16_tx_fault,	 0, 0, 0),
	mk_gpio_pin(217,  4, port13_present,	 0, 1, 0),
	mk_gpio_pin(217,  5, port14_present,	 0, 1, 0),
	mk_gpio_pin(217,  6, port15_present,	 0, 1, 0),
	mk_gpio_pin(217,  7, port16_present,	 0, 1, 0),
	mk_gpio_pin(217,  8, port13_tx_disable,	 1, 0, 0),
	mk_gpio_pin(217,  9, port14_tx_disable,	 1, 0, 0),
	mk_gpio_pin(217, 10, port15_tx_disable,	 1, 0, 0),
	mk_gpio_pin(217, 11, port16_tx_disable,	 1, 0, 0),
	mk_gpio_pin(217, 12, port13_rx_los,	 0, 0, 0),
	mk_gpio_pin(217, 13, port14_rx_los,	 0, 0, 0),
	mk_gpio_pin(217, 14, port15_rx_los,	 0, 0, 0),
	mk_gpio_pin(217, 15, port16_rx_los,	 0, 0, 0),
};

mk_gpio_pins(gpio222) = {
	mk_gpio_pin(233,  0, port9_rs0,		1, 0, 0),
	mk_gpio_pin(233,  1, port9_rs1,		1, 0, 0),
	mk_gpio_pin(233,  2, port10_rs0,	 1, 0, 0),
	mk_gpio_pin(233,  3, port10_rs1,	 1, 0, 0),
	mk_gpio_pin(233,  4, port11_rs0,	 1, 0, 0),
	mk_gpio_pin(233,  5, port11_rs1,	 1, 0, 0),
	mk_gpio_pin(233,  6, port12_rs0,	 1, 0, 0),
	mk_gpio_pin(233,  7, port12_rs1,	 1, 0, 0),
	mk_gpio_pin(233,  8, port13_rs0,	 1, 0, 0),
	mk_gpio_pin(233,  9, port13_rs1,	 1, 0, 0),
	mk_gpio_pin(233, 10, port14_rs0,	 1, 0, 0),
	mk_gpio_pin(233, 11, port14_rs1,	 1, 0, 0),
	mk_gpio_pin(233, 12, port15_rs0,	 1, 0, 0),
	mk_gpio_pin(233, 13, port15_rs1,	 1, 0, 0),
	mk_gpio_pin(233, 14, port16_rs0,	 1, 0, 0),
	mk_gpio_pin(233, 15, port16_rs1,	 1, 0, 0),
};

/* --- mux channel 3 --- */
mk_gpio_pins(gpio320) = {
	mk_gpio_pin(301,  0, port17_tx_fault,	 0, 0, 0),
	mk_gpio_pin(301,  1, port18_tx_fault,	 0, 0, 0),
	mk_gpio_pin(301,  2, port19_tx_fault,	 0, 0, 0),
	mk_gpio_pin(301,  3, port20_tx_fault,	 0, 0, 0),
	mk_gpio_pin(301,  4, port17_present,	 0, 1, 0),
	mk_gpio_pin(301,  5, port18_present,	 0, 1, 0),
	mk_gpio_pin(301,  6, port19_present,	 0, 1, 0),
	mk_gpio_pin(301,  7, port20_present,	 0, 1, 0),
	mk_gpio_pin(301,  8, port17_tx_disable,	 1, 0, 0),
	mk_gpio_pin(301,  9, port18_tx_disable,	 1, 0, 0),
	mk_gpio_pin(301, 10, port19_tx_disable,	 1, 0, 0),
	mk_gpio_pin(301, 11, port20_tx_disable,	 1, 0, 0),
	mk_gpio_pin(301, 12, port17_rx_los,	 0, 0, 0),
	mk_gpio_pin(301, 13, port18_rx_los,	 0, 0, 0),
	mk_gpio_pin(301, 14, port19_rx_los,	 0, 0, 0),
	mk_gpio_pin(301, 15, port20_rx_los,	 0, 0, 0),
};

mk_gpio_pins(gpio321) = {
	mk_gpio_pin(317,  0, port21_tx_fault,	 0, 0, 0),
	mk_gpio_pin(317,  1, port22_tx_fault,	 0, 0, 0),
	mk_gpio_pin(317,  2, port23_tx_fault,	 0, 0, 0),
	mk_gpio_pin(317,  3, port24_tx_fault,	 0, 0, 0),
	mk_gpio_pin(317,  4, port21_present,	 0, 1, 0),
	mk_gpio_pin(317,  5, port22_present,	 0, 1, 0),
	mk_gpio_pin(317,  6, port23_present,	 0, 1, 0),
	mk_gpio_pin(317,  7, port24_present,	 0, 1, 0),
	mk_gpio_pin(317,  8, port21_tx_disable,	 1, 0, 0),
	mk_gpio_pin(317,  9, port22_tx_disable,	 1, 0, 0),
	mk_gpio_pin(317, 10, port23_tx_disable,	 1, 0, 0),
	mk_gpio_pin(317, 11, port24_tx_disable,	 1, 0, 0),
	mk_gpio_pin(317, 12, port21_rx_los,	 0, 0, 0),
	mk_gpio_pin(317, 13, port22_rx_los,	 0, 0, 0),
	mk_gpio_pin(317, 14, port23_rx_los,	 0, 0, 0),
	mk_gpio_pin(317, 15, port24_rx_los,	 0, 0, 0),
};

mk_gpio_pins(gpio322) = {
	mk_gpio_pin(333,  0, port17_rs0,	 1, 0, 0),
	mk_gpio_pin(333,  1, port17_rs1,	 1, 0, 0),
	mk_gpio_pin(333,  2, port18_rs0,	 1, 0, 0),
	mk_gpio_pin(333,  3, port18_rs1,	 1, 0, 0),
	mk_gpio_pin(333,  4, port19_rs0,	 1, 0, 0),
	mk_gpio_pin(333,  5, port19_rs1,	 1, 0, 0),
	mk_gpio_pin(333,  6, port20_rs0,	 1, 0, 0),
	mk_gpio_pin(333,  7, port20_rs1,	 1, 0, 0),
	mk_gpio_pin(333,  8, port21_rs0,	 1, 0, 0),
	mk_gpio_pin(333,  9, port21_rs1,	 1, 0, 0),
	mk_gpio_pin(333, 10, port22_rs0,	 1, 0, 0),
	mk_gpio_pin(333, 11, port22_rs1,	 1, 0, 0),
	mk_gpio_pin(333, 12, port23_rs0,	 1, 0, 0),
	mk_gpio_pin(333, 13, port23_rs1,	 1, 0, 0),
	mk_gpio_pin(333, 14, port24_rs0,	 1, 0, 0),
	mk_gpio_pin(333, 15, port24_rs1,	 1, 0, 0),
};

/* --- mux channel 4 --- */
mk_gpio_pins(gpio420) = {
	mk_gpio_pin(401,  0, port25_tx_fault,	 0, 0, 0),
	mk_gpio_pin(401,  1, port26_tx_fault,	 0, 0, 0),
	mk_gpio_pin(401,  2, port27_tx_fault,	 0, 0, 0),
	mk_gpio_pin(401,  3, port28_tx_fault,	 0, 0, 0),
	mk_gpio_pin(401,  4, port25_present,	 0, 1, 0),
	mk_gpio_pin(401,  5, port26_present,	 0, 1, 0),
	mk_gpio_pin(401,  6, port27_present,	 0, 1, 0),
	mk_gpio_pin(401,  7, port28_present,	 0, 1, 0),
	mk_gpio_pin(401,  8, port25_tx_disable,	 1, 0, 0),
	mk_gpio_pin(401,  9, port26_tx_disable,	 1, 0, 0),
	mk_gpio_pin(401, 10, port27_tx_disable,	 1, 0, 0),
	mk_gpio_pin(401, 11, port28_tx_disable,	 1, 0, 0),
	mk_gpio_pin(401, 12, port25_rx_los,	 0, 0, 0),
	mk_gpio_pin(401, 13, port26_rx_los,	 0, 0, 0),
	mk_gpio_pin(401, 14, port27_rx_los,	 0, 0, 0),
	mk_gpio_pin(401, 15, port28_rx_los,	 0, 0, 0),
};

mk_gpio_pins(gpio421) = {
	mk_gpio_pin(417,  0, port29_tx_fault,	 0, 0, 0),
	mk_gpio_pin(417,  1, port30_tx_fault,	 0, 0, 0),
	mk_gpio_pin(417,  2, port31_tx_fault,	 0, 0, 0),
	mk_gpio_pin(417,  3, port32_tx_fault,	 0, 0, 0),
	mk_gpio_pin(417,  4, port29_present,	 0, 1, 0),
	mk_gpio_pin(417,  5, port30_present,	 0, 1, 0),
	mk_gpio_pin(417,  6, port31_present,	 0, 1, 0),
	mk_gpio_pin(417,  7, port32_present,	 0, 1, 0),
	mk_gpio_pin(417,  8, port29_tx_disable,	 1, 0, 0),
	mk_gpio_pin(417,  9, port30_tx_disable,	 1, 0, 0),
	mk_gpio_pin(417, 10, port31_tx_disable,	 1, 0, 0),
	mk_gpio_pin(417, 11, port32_tx_disable,	 1, 0, 0),
	mk_gpio_pin(417, 12, port29_rx_los,	 0, 0, 0),
	mk_gpio_pin(417, 13, port30_rx_los,	 0, 0, 0),
	mk_gpio_pin(417, 14, port31_rx_los,	 0, 0, 0),
	mk_gpio_pin(417, 15, port32_rx_los,	 0, 0, 0),
};

mk_gpio_pins(gpio422) = {
	mk_gpio_pin(433,  0, port25_rs0,	 1, 0, 0),
	mk_gpio_pin(433,  1, port25_rs1,	 1, 0, 0),
	mk_gpio_pin(433,  2, port26_rs0,	 1, 0, 0),
	mk_gpio_pin(433,  3, port26_rs1,	 1, 0, 0),
	mk_gpio_pin(433,  4, port27_rs0,	 1, 0, 0),
	mk_gpio_pin(433,  5, port27_rs1,	 1, 0, 0),
	mk_gpio_pin(433,  6, port28_rs0,	 1, 0, 0),
	mk_gpio_pin(433,  7, port28_rs1,	 1, 0, 0),
	mk_gpio_pin(433,  8, port29_rs0,	 1, 0, 0),
	mk_gpio_pin(433,  9, port29_rs1,	 1, 0, 0),
	mk_gpio_pin(433, 10, port30_rs0,	 1, 0, 0),
	mk_gpio_pin(433, 11, port30_rs1,	 1, 0, 0),
	mk_gpio_pin(433, 12, port31_rs0,	 1, 0, 0),
	mk_gpio_pin(433, 13, port31_rs1,	 1, 0, 0),
	mk_gpio_pin(433, 14, port32_rs0,	 1, 0, 0),
	mk_gpio_pin(433, 15, port32_rs1,	 1, 0, 0),
};

/* --- mux channel 5 --- */
mk_gpio_pins(gpio520) = {
	mk_gpio_pin(501,  0, port33_tx_fault,	 0, 0, 0),
	mk_gpio_pin(501,  1, port34_tx_fault,	 0, 0, 0),
	mk_gpio_pin(501,  2, port35_tx_fault,	 0, 0, 0),
	mk_gpio_pin(501,  3, port36_tx_fault,	 0, 0, 0),
	mk_gpio_pin(501,  4, port33_present,	 0, 1, 0),
	mk_gpio_pin(501,  5, port34_present,	 0, 1, 0),
	mk_gpio_pin(501,  6, port35_present,	 0, 1, 0),
	mk_gpio_pin(501,  7, port36_present,	 0, 1, 0),
	mk_gpio_pin(501,  8, port33_tx_disable,	 1, 0, 0),
	mk_gpio_pin(501,  9, port34_tx_disable,	 1, 0, 0),
	mk_gpio_pin(501, 10, port35_tx_disable,	 1, 0, 0),
	mk_gpio_pin(501, 11, port36_tx_disable,	 1, 0, 0),
	mk_gpio_pin(501, 12, port33_rx_los,	 0, 0, 0),
	mk_gpio_pin(501, 13, port34_rx_los,	 0, 0, 0),
	mk_gpio_pin(501, 14, port35_rx_los,	 0, 0, 0),
	mk_gpio_pin(501, 15, port36_rx_los,	 0, 0, 0),
};

mk_gpio_pins(gpio521) = {
	mk_gpio_pin(517,  0, port37_tx_fault,	 0, 0, 0),
	mk_gpio_pin(517,  1, port38_tx_fault,	 0, 0, 0),
	mk_gpio_pin(517,  2, port39_tx_fault,	 0, 0, 0),
	mk_gpio_pin(517,  3, port40_tx_fault,	 0, 0, 0),
	mk_gpio_pin(517,  4, port37_present,	 0, 1, 0),
	mk_gpio_pin(517,  5, port38_present,	 0, 1, 0),
	mk_gpio_pin(517,  6, port39_present,	 0, 1, 0),
	mk_gpio_pin(517,  7, port40_present,	 0, 1, 0),
	mk_gpio_pin(517,  8, port37_tx_disable,	 1, 0, 0),
	mk_gpio_pin(517,  9, port38_tx_disable,	 1, 0, 0),
	mk_gpio_pin(517, 10, port39_tx_disable,	 1, 0, 0),
	mk_gpio_pin(517, 11, port40_tx_disable,	 1, 0, 0),
	mk_gpio_pin(517, 12, port37_rx_los,	 0, 0, 0),
	mk_gpio_pin(517, 13, port38_rx_los,	 0, 0, 0),
	mk_gpio_pin(517, 14, port39_rx_los,	 0, 0, 0),
	mk_gpio_pin(517, 15, port40_rx_los,	 0, 0, 0),
};

mk_gpio_pins(gpio522) = {
	mk_gpio_pin(533,  0, port33_rs0,	 1, 0, 0),
	mk_gpio_pin(533,  1, port33_rs1,	 1, 0, 0),
	mk_gpio_pin(533,  2, port34_rs0,	 1, 0, 0),
	mk_gpio_pin(533,  3, port34_rs1,	 1, 0, 0),
	mk_gpio_pin(533,  4, port35_rs0,	 1, 0, 0),
	mk_gpio_pin(533,  5, port35_rs1,	 1, 0, 0),
	mk_gpio_pin(533,  6, port36_rs0,	 1, 0, 0),
	mk_gpio_pin(533,  7, port36_rs1,	 1, 0, 0),
	mk_gpio_pin(533,  8, port37_rs0,	 1, 0, 0),
	mk_gpio_pin(533,  9, port37_rs1,	 1, 0, 0),
	mk_gpio_pin(533, 10, port38_rs0,	 1, 0, 0),
	mk_gpio_pin(533, 11, port38_rs1,	 1, 0, 0),
	mk_gpio_pin(533, 12, port39_rs0,	 1, 0, 0),
	mk_gpio_pin(533, 13, port39_rs1,	 1, 0, 0),
	mk_gpio_pin(533, 14, port40_rs0,	 1, 0, 0),
	mk_gpio_pin(533, 15, port40_rs1,	 1, 0, 0),
};

/* --- mux channel 6 --- */
mk_gpio_pins(gpio620) = {
	mk_gpio_pin(601,  0, port41_tx_fault,	 0, 0, 0),
	mk_gpio_pin(601,  1, port42_tx_fault,	 0, 0, 0),
	mk_gpio_pin(601,  2, port43_tx_fault,	 0, 0, 0),
	mk_gpio_pin(601,  3, port44_tx_fault,	 0, 0, 0),
	mk_gpio_pin(601,  4, port41_present,	 0, 1, 0),
	mk_gpio_pin(601,  5, port42_present,	 0, 1, 0),
	mk_gpio_pin(601,  6, port43_present,	 0, 1, 0),
	mk_gpio_pin(601,  7, port44_present,	 0, 1, 0),
	mk_gpio_pin(601,  8, port41_tx_disable,	 1, 0, 0),
	mk_gpio_pin(601,  9, port42_tx_disable,	 1, 0, 0),
	mk_gpio_pin(601, 10, port43_tx_disable,	 1, 0, 0),
	mk_gpio_pin(601, 11, port44_tx_disable,	 1, 0, 0),
	mk_gpio_pin(601, 12, port41_rx_los,	 0, 0, 0),
	mk_gpio_pin(601, 13, port42_rx_los,	 0, 0, 0),
	mk_gpio_pin(601, 14, port43_rx_los,	 0, 0, 0),
	mk_gpio_pin(601, 15, port44_rx_los,	 0, 0, 0),
};

mk_gpio_pins(gpio621) = {
	mk_gpio_pin(617,  0, port45_tx_fault,	 0, 0, 0),
	mk_gpio_pin(617,  1, port46_tx_fault,	 0, 0, 0),
	mk_gpio_pin(617,  2, port47_tx_fault,	 0, 0, 0),
	mk_gpio_pin(617,  3, port48_tx_fault,	 0, 0, 0),
	mk_gpio_pin(617,  4, port45_present,	 0, 1, 0),
	mk_gpio_pin(617,  5, port46_present,	 0, 1, 0),
	mk_gpio_pin(617,  6, port47_present,	 0, 1, 0),
	mk_gpio_pin(617,  7, port48_present,	 0, 1, 0),
	mk_gpio_pin(617,  8, port45_tx_disable,	 1, 0, 0),
	mk_gpio_pin(617,  9, port46_tx_disable,	 1, 0, 0),
	mk_gpio_pin(617, 10, port47_tx_disable,	 1, 0, 0),
	mk_gpio_pin(617, 11, port48_tx_disable,	 1, 0, 0),
	mk_gpio_pin(617, 12, port45_rx_los,	 0, 0, 0),
	mk_gpio_pin(617, 13, port46_rx_los,	 0, 0, 0),
	mk_gpio_pin(617, 14, port47_rx_los,	 0, 0, 0),
	mk_gpio_pin(617, 15, port48_rx_los,	 0, 0, 0),
};

mk_gpio_pins(gpio622) = {
	mk_gpio_pin(633,  0, port41_rs0,	 1, 0, 0),
	mk_gpio_pin(633,  1, port41_rs1,	 1, 0, 0),
	mk_gpio_pin(633,  2, port42_rs0,	 1, 0, 0),
	mk_gpio_pin(633,  3, port42_rs1,	 1, 0, 0),
	mk_gpio_pin(633,  4, port43_rs0,	 1, 0, 0),
	mk_gpio_pin(633,  5, port43_rs1,	 1, 0, 0),
	mk_gpio_pin(633,  6, port44_rs0,	 1, 0, 0),
	mk_gpio_pin(633,  7, port44_rs1,	 1, 0, 0),
	mk_gpio_pin(633,  8, port45_rs0,	 1, 0, 0),
	mk_gpio_pin(633,  9, port45_rs1,	 1, 0, 0),
	mk_gpio_pin(633, 10, port46_rs0,	 1, 0, 0),
	mk_gpio_pin(633, 11, port46_rs1,	 1, 0, 0),
	mk_gpio_pin(633, 12, port47_rs0,	 1, 0, 0),
	mk_gpio_pin(633, 13, port47_rs1,	 1, 0, 0),
	mk_gpio_pin(633, 14, port48_rs0,	 1, 0, 0),
	mk_gpio_pin(633, 15, port48_rs1,	 1, 0, 0),
};

mk_gpio_platform_data(gpio020,	 1, 16);
mk_gpio_platform_data(gpio021,	17, 16);
mk_gpio_platform_data(gpio022,	33, 16);

mk_gpio_platform_data(gpio120, 101, 16);
mk_gpio_platform_data(gpio121, 117, 16);
mk_gpio_platform_data(gpio122, 133, 16);

mk_gpio_platform_data(gpio220, 201, 16);
mk_gpio_platform_data(gpio221, 217, 16);
mk_gpio_platform_data(gpio222, 233, 16);

mk_gpio_platform_data(gpio320, 301, 16);
mk_gpio_platform_data(gpio321, 317, 16);
mk_gpio_platform_data(gpio322, 333, 16);

mk_gpio_platform_data(gpio420, 401, 16);
mk_gpio_platform_data(gpio421, 417, 16);
mk_gpio_platform_data(gpio422, 433, 16);

mk_gpio_platform_data(gpio520, 501, 16);
mk_gpio_platform_data(gpio521, 517, 16);
mk_gpio_platform_data(gpio522, 533, 16);

mk_gpio_platform_data(gpio620, 601, 16);
mk_gpio_platform_data(gpio621, 617, 16);
mk_gpio_platform_data(gpio622, 633, 16);

/*
 * Main i2c device table
 *
 * We use the mk_i2cdev() macro to construct the entries.
 * Each entry is a bus number and a i2c_board_info.
 * The i2c_board_info structure specifies the device type, address,
 * and platform data specific to the device type.
 */

static struct platform_i2c_device_info i2c_devices[] = {
	mk_i2cdev(I2C_I801_BUS,	 "24c02",	 0x54, &swconfig_54_at24),
	mk_i2cdev(I2C_I801_BUS,	 "24c02",	 0x55, &vpd_55_at24),
	mk_i2cdev(I2C_I801_BUS,	 "24c02",	 0x56, &bios_56_at24),
	mk_i2cdev(I2C_I801_BUS,	 "24c02",	 0x57, &board_57_at24),

	mk_i2cdev(I2C_I801_BUS,	 NE2580_CPLD1_NAME, 0x77, NULL),
	mk_i2cdev(I2C_I801_BUS,	 NE2580_CPLD2_NAME, 0x33, NULL),

	mk_i2cdev(I2C_I801_BUS,	 "pca9543",	 0x73, &mux1_platform_data),
	mk_i2cdev(I2C_I801_BUS,	 "pca9548",	 0x70, &mux2_platform_data),

	mk_i2cdev(I2C_MUX1_BUS0, "24c02",	 0x52, &psu1_52_at24),
	mk_i2cdev(I2C_MUX1_BUS0, "24c02",	 0x53, &psu2_53_at24),

	mk_i2cdev(I2C_MUX1_BUS0, "dps550",	 0x5a, NULL),
	mk_i2cdev(I2C_MUX1_BUS0, "dps550",	 0x5b, NULL),

	mk_i2cdev(I2C_MUX1_BUS1, "tmp75",	 0x4a, NULL),
	mk_i2cdev(I2C_MUX1_BUS1, "tmp75",	 0x4d, NULL),
	mk_i2cdev(I2C_MUX1_BUS1, "tmp75",	 0x4e, NULL),
	mk_i2cdev(I2C_MUX1_BUS1, "tmp75",	 0x48, NULL),

	mk_i2cdev(I2C_MUX2_BUS0, "pca9548",	 0x72, &mux3_platform_data),
	mk_i2cdev(I2C_MUX2_BUS1, "pca9548",	 0x72, &mux4_platform_data),
	mk_i2cdev(I2C_MUX2_BUS2, "pca9548",	 0x72, &mux5_platform_data),
	mk_i2cdev(I2C_MUX2_BUS3, "pca9548",	 0x72, &mux6_platform_data),
	mk_i2cdev(I2C_MUX2_BUS4, "pca9548",	 0x72, &mux7_platform_data),
	mk_i2cdev(I2C_MUX2_BUS5, "pca9548",	 0x72, &mux8_platform_data),
	mk_i2cdev(I2C_MUX2_BUS6, "pca9548",	 0x72, &mux9_platform_data),

	/* QSFP28 ports 49-56 */
	mk_i2cdev(I2C_MUX3_BUS0, "sff8436",	 0x50, &port49_50_sff8436),
	mk_i2cdev(I2C_MUX3_BUS1, "sff8436",	 0x50, &port50_50_sff8436),
	mk_i2cdev(I2C_MUX3_BUS2, "sff8436",	 0x50, &port51_50_sff8436),
	mk_i2cdev(I2C_MUX3_BUS3, "sff8436",	 0x50, &port52_50_sff8436),
	mk_i2cdev(I2C_MUX3_BUS4, "sff8436",	 0x50, &port53_50_sff8436),
	mk_i2cdev(I2C_MUX3_BUS5, "sff8436",	 0x50, &port54_50_sff8436),
	mk_i2cdev(I2C_MUX3_BUS6, "sff8436",	 0x50, &port55_50_sff8436),
	mk_i2cdev(I2C_MUX3_BUS7, "sff8436",	 0x50, &port56_50_sff8436),
	mk_i2cdev(I2C_MUX2_BUS0, "pca9555",	 0x20, &gpio020_platform_data),
	mk_i2cdev(I2C_MUX2_BUS0, "pca9555",	 0x21, &gpio021_platform_data),
	mk_i2cdev(I2C_MUX2_BUS0, "pca9555",	 0x22, &gpio022_platform_data),

	/* SFP28 ports 1-8 */
	mk_i2cdev(I2C_MUX4_BUS0, "24c04",	 0x50, &port1_50_at24),
	mk_i2cdev(I2C_MUX4_BUS1, "24c04",	 0x50, &port2_50_at24),
	mk_i2cdev(I2C_MUX4_BUS2, "24c04",	 0x50, &port3_50_at24),
	mk_i2cdev(I2C_MUX4_BUS3, "24c04",	 0x50, &port4_50_at24),
	mk_i2cdev(I2C_MUX4_BUS4, "24c04",	 0x50, &port5_50_at24),
	mk_i2cdev(I2C_MUX4_BUS5, "24c04",	 0x50, &port6_50_at24),
	mk_i2cdev(I2C_MUX4_BUS6, "24c04",	 0x50, &port7_50_at24),
	mk_i2cdev(I2C_MUX4_BUS7, "24c04",	 0x50, &port8_50_at24),
	mk_i2cdev(I2C_MUX2_BUS1, "pca9555",	 0x20, &gpio120_platform_data),
	mk_i2cdev(I2C_MUX2_BUS1, "pca9555",	 0x21, &gpio121_platform_data),
	mk_i2cdev(I2C_MUX2_BUS1, "pca9555",	 0x22, &gpio122_platform_data),

	/* SFP28 ports 9-16 */
	mk_i2cdev(I2C_MUX5_BUS0, "24c04",	 0x50, &port9_50_at24),
	mk_i2cdev(I2C_MUX5_BUS1, "24c04",	 0x50, &port10_50_at24),
	mk_i2cdev(I2C_MUX5_BUS2, "24c04",	 0x50, &port11_50_at24),
	mk_i2cdev(I2C_MUX5_BUS3, "24c04",	 0x50, &port12_50_at24),
	mk_i2cdev(I2C_MUX5_BUS4, "24c04",	 0x50, &port13_50_at24),
	mk_i2cdev(I2C_MUX5_BUS5, "24c04",	 0x50, &port14_50_at24),
	mk_i2cdev(I2C_MUX5_BUS6, "24c04",	 0x50, &port15_50_at24),
	mk_i2cdev(I2C_MUX5_BUS7, "24c04",	 0x50, &port16_50_at24),
	mk_i2cdev(I2C_MUX2_BUS2, "pca9555",	 0x20, &gpio220_platform_data),
	mk_i2cdev(I2C_MUX2_BUS2, "pca9555",	 0x21, &gpio221_platform_data),
	mk_i2cdev(I2C_MUX2_BUS2, "pca9555",	 0x22, &gpio222_platform_data),

	/* SFP28 ports 17-24 */
	mk_i2cdev(I2C_MUX6_BUS0, "24c04",	 0x50, &port17_50_at24),
	mk_i2cdev(I2C_MUX6_BUS1, "24c04",	 0x50, &port18_50_at24),
	mk_i2cdev(I2C_MUX6_BUS2, "24c04",	 0x50, &port19_50_at24),
	mk_i2cdev(I2C_MUX6_BUS3, "24c04",	 0x50, &port20_50_at24),
	mk_i2cdev(I2C_MUX6_BUS4, "24c04",	 0x50, &port21_50_at24),
	mk_i2cdev(I2C_MUX6_BUS5, "24c04",	 0x50, &port22_50_at24),
	mk_i2cdev(I2C_MUX6_BUS6, "24c04",	 0x50, &port23_50_at24),
	mk_i2cdev(I2C_MUX6_BUS7, "24c04",	 0x50, &port24_50_at24),
	mk_i2cdev(I2C_MUX2_BUS3, "pca9555",	 0x20, &gpio320_platform_data),
	mk_i2cdev(I2C_MUX2_BUS3, "pca9555",	 0x21, &gpio321_platform_data),
	mk_i2cdev(I2C_MUX2_BUS3, "pca9555",	 0x22, &gpio322_platform_data),

	/* SFP28 ports 25-32 */
	mk_i2cdev(I2C_MUX7_BUS0, "24c04",	 0x50, &port25_50_at24),
	mk_i2cdev(I2C_MUX7_BUS1, "24c04",	 0x50, &port26_50_at24),
	mk_i2cdev(I2C_MUX7_BUS2, "24c04",	 0x50, &port27_50_at24),
	mk_i2cdev(I2C_MUX7_BUS3, "24c04",	 0x50, &port28_50_at24),
	mk_i2cdev(I2C_MUX7_BUS4, "24c04",	 0x50, &port29_50_at24),
	mk_i2cdev(I2C_MUX7_BUS5, "24c04",	 0x50, &port30_50_at24),
	mk_i2cdev(I2C_MUX7_BUS6, "24c04",	 0x50, &port31_50_at24),
	mk_i2cdev(I2C_MUX7_BUS7, "24c04",	 0x50, &port32_50_at24),
	mk_i2cdev(I2C_MUX2_BUS4, "pca9555",	 0x20, &gpio420_platform_data),
	mk_i2cdev(I2C_MUX2_BUS4, "pca9555",	 0x21, &gpio421_platform_data),
	mk_i2cdev(I2C_MUX2_BUS4, "pca9555",	 0x22, &gpio422_platform_data),

	/* SFP28 ports 33-40 */
	mk_i2cdev(I2C_MUX8_BUS0, "24c04",	 0x50, &port33_50_at24),
	mk_i2cdev(I2C_MUX8_BUS1, "24c04",	 0x50, &port34_50_at24),
	mk_i2cdev(I2C_MUX8_BUS2, "24c04",	 0x50, &port35_50_at24),
	mk_i2cdev(I2C_MUX8_BUS3, "24c04",	 0x50, &port36_50_at24),
	mk_i2cdev(I2C_MUX8_BUS4, "24c04",	 0x50, &port37_50_at24),
	mk_i2cdev(I2C_MUX8_BUS5, "24c04",	 0x50, &port38_50_at24),
	mk_i2cdev(I2C_MUX8_BUS6, "24c04",	 0x50, &port39_50_at24),
	mk_i2cdev(I2C_MUX8_BUS7, "24c04",	 0x50, &port40_50_at24),
	mk_i2cdev(I2C_MUX2_BUS5, "pca9555",	 0x20, &gpio520_platform_data),
	mk_i2cdev(I2C_MUX2_BUS5, "pca9555",	 0x21, &gpio521_platform_data),
	mk_i2cdev(I2C_MUX2_BUS5, "pca9555",	 0x22, &gpio522_platform_data),

	/* SFP28 ports 41-48 */
	mk_i2cdev(I2C_MUX9_BUS0, "24c04",	 0x50, &port41_50_at24),
	mk_i2cdev(I2C_MUX9_BUS1, "24c04",	 0x50, &port42_50_at24),
	mk_i2cdev(I2C_MUX9_BUS2, "24c04",	 0x50, &port43_50_at24),
	mk_i2cdev(I2C_MUX9_BUS3, "24c04",	 0x50, &port44_50_at24),
	mk_i2cdev(I2C_MUX9_BUS4, "24c04",	 0x50, &port45_50_at24),
	mk_i2cdev(I2C_MUX9_BUS5, "24c04",	 0x50, &port46_50_at24),
	mk_i2cdev(I2C_MUX9_BUS6, "24c04",	 0x50, &port47_50_at24),
	mk_i2cdev(I2C_MUX9_BUS7, "24c04",	 0x50, &port48_50_at24),
	mk_i2cdev(I2C_MUX2_BUS6, "pca9555",	 0x20, &gpio620_platform_data),
	mk_i2cdev(I2C_MUX2_BUS6, "pca9555",	 0x21, &gpio621_platform_data),
	mk_i2cdev(I2C_MUX2_BUS6, "pca9555",	 0x22, &gpio622_platform_data),
};

/* gpio init */

static int init_gpio_pins(struct gpio_pin *pins, int num_pins)
{
	int i;
	int ret;

	pr_info("Registering %d GPIO pins\n", num_pins);
	for (i = 0; i < num_pins; i++) {
		struct gpio_pin *pin = pins + i;

		ret = gpio_request(pin->num, NULL);

		if (ret) {
			if (ret != -EPROBE_DEFER)
				pr_err(DRIVER_NAME ": failed to request GPIO pin %s (%d), err %d\n",
				       pin->name, pin->num, ret);
			goto err_exit;
		}

		pin->allocated = true;

		if (pin->output)
			ret = gpio_direction_output(pin->num, pin->value);
		else
			ret = gpio_direction_input(pin->num);
		if (ret) {
			pr_err("failed to set direction on GPIO pin %s (%u), err %d\n",
			       pin->name, pin->num, ret);
			return ret;
		}

		ret = gpio_sysfs_set_active_low(pin->num, pin->active_low);
		if (ret) {
			pr_err("failed to set active_low on GPIO pin %s (%u), err %d\n",
			       pin->name, pin->num, ret);
			return ret;
		}

		ret = gpio_export(pin->num, false);
		if (ret) {
			pr_err("failed to export GPIO pin %s (%u), err %d\n",
			       pin->name, pin->num, ret);
			return ret;
		pr_info("GPIO pin %s (%d) initialized\n", pin->name, pin->num);
		}
	}
	return 0;

err_exit:
	while (--i >= 0) {
		gpio_free(pins[i].num);
		pins[i].allocated = false;
	}
	return ret;
}

static void free_gpio_pins(struct gpio_pin *pins, int num_pins)
{
	int i;

	for (i = 0; i < num_pins; i++)
		if (pins[i].allocated) {
			gpio_free(pins[i].num);
			pins[i].allocated = false;
		}
}

static void gpio_exit(void)
{
	free_gpio_pins(gpio622_pins, ARRAY_SIZE(gpio622_pins));
	free_gpio_pins(gpio621_pins, ARRAY_SIZE(gpio621_pins));
	free_gpio_pins(gpio620_pins, ARRAY_SIZE(gpio620_pins));
	free_gpio_pins(gpio522_pins, ARRAY_SIZE(gpio522_pins));
	free_gpio_pins(gpio521_pins, ARRAY_SIZE(gpio521_pins));
	free_gpio_pins(gpio520_pins, ARRAY_SIZE(gpio520_pins));
	free_gpio_pins(gpio422_pins, ARRAY_SIZE(gpio422_pins));
	free_gpio_pins(gpio421_pins, ARRAY_SIZE(gpio421_pins));
	free_gpio_pins(gpio420_pins, ARRAY_SIZE(gpio420_pins));
	free_gpio_pins(gpio322_pins, ARRAY_SIZE(gpio322_pins));
	free_gpio_pins(gpio321_pins, ARRAY_SIZE(gpio321_pins));
	free_gpio_pins(gpio320_pins, ARRAY_SIZE(gpio320_pins));
	free_gpio_pins(gpio222_pins, ARRAY_SIZE(gpio222_pins));
	free_gpio_pins(gpio221_pins, ARRAY_SIZE(gpio221_pins));
	free_gpio_pins(gpio220_pins, ARRAY_SIZE(gpio220_pins));
	free_gpio_pins(gpio122_pins, ARRAY_SIZE(gpio122_pins));
	free_gpio_pins(gpio121_pins, ARRAY_SIZE(gpio121_pins));
	free_gpio_pins(gpio120_pins, ARRAY_SIZE(gpio120_pins));
	free_gpio_pins(gpio022_pins, ARRAY_SIZE(gpio022_pins));
	free_gpio_pins(gpio021_pins, ARRAY_SIZE(gpio021_pins));
	free_gpio_pins(gpio020_pins, ARRAY_SIZE(gpio020_pins));
}

static int gpio_init(void)
{
	int ret;

	ret = init_gpio_pins(gpio020_pins, ARRAY_SIZE(gpio020_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio021_pins, ARRAY_SIZE(gpio021_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio022_pins, ARRAY_SIZE(gpio022_pins));
	if (ret)
		goto err_exit;

	ret = init_gpio_pins(gpio120_pins, ARRAY_SIZE(gpio120_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio121_pins, ARRAY_SIZE(gpio121_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio122_pins, ARRAY_SIZE(gpio122_pins));
	if (ret)
		goto err_exit;

	ret = init_gpio_pins(gpio220_pins, ARRAY_SIZE(gpio220_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio221_pins, ARRAY_SIZE(gpio221_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio222_pins, ARRAY_SIZE(gpio222_pins));
	if (ret)
		goto err_exit;

	ret = init_gpio_pins(gpio320_pins, ARRAY_SIZE(gpio320_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio321_pins, ARRAY_SIZE(gpio321_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio322_pins, ARRAY_SIZE(gpio322_pins));
	if (ret)
		goto err_exit;

	ret = init_gpio_pins(gpio420_pins, ARRAY_SIZE(gpio420_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio421_pins, ARRAY_SIZE(gpio421_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio422_pins, ARRAY_SIZE(gpio422_pins));
	if (ret)
		goto err_exit;

	ret = init_gpio_pins(gpio520_pins, ARRAY_SIZE(gpio520_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio521_pins, ARRAY_SIZE(gpio521_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio522_pins, ARRAY_SIZE(gpio522_pins));
	if (ret)
		goto err_exit;

	ret = init_gpio_pins(gpio620_pins, ARRAY_SIZE(gpio620_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio621_pins, ARRAY_SIZE(gpio621_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio622_pins, ARRAY_SIZE(gpio622_pins));
	if (ret)
		goto err_exit;

	return 0;

err_exit:
	gpio_exit();
	return ret;
}

/* i2c init */

static void init_gpio_platform_data(struct gpio_pin *pins,
				    int num_pins,
				    struct pca953x_platform_data *pdata)
{
	int i;
	/* pdata->names is *const*, we have to cast it */
	const char **names = (const char **)pdata->names;

	for (i = 0; i < num_pins; i++)
		names[pins[i].pin] = pins[i].name;
}

static void free_i2c_devices(void)
{
	int i;

	for (i = ARRAY_SIZE(i2c_devices); --i >= 0;) {
		if (i2c_devices[i].client) {
			i2c_unregister_device(i2c_devices[i].client);
			i2c_devices[i].client = NULL;
		}
	}
}

static int init_i2c_devices(void)
{
	int i801_bus;
	int ret;
	int i = 0;

	i801_bus = cumulus_i2c_find_adapter(I801_ADAPTER_NAME);
	if (i801_bus < 0) {
		pr_err(DRIVER_NAME ": could not find the i801 adapter bus\n");
		ret = -ENODEV;
		goto err_exit;
	}

	for (i = 0; i < ARRAY_SIZE(i2c_devices); i++) {
		int bus = i2c_devices[i].bus;
		struct i2c_client *client;

		/* skip this client if it's already been added */
		if (i2c_devices[i].client)
			continue;

		if (bus == I2C_I801_BUS)
			bus = i801_bus;

		client = cumulus_i2c_add_client(bus,
						&i2c_devices[i].board_info);
		if (IS_ERR(client)) {
			ret = PTR_ERR(client);
			goto err_exit;
		}
		i2c_devices[i].client = client;
	}
	return 0;

err_exit:
	while (--i >= 0) {
		if (i2c_devices[i].client) {
			i2c_unregister_device(i2c_devices[i].client);
			i2c_devices[i].client = NULL;
		}
	}
	return ret;
}

static int i2c_init(void)
{
	int ret;

	init_gpio_platform_data(gpio020_pins, ARRAY_SIZE(gpio020_pins),
				&gpio020_platform_data);
	init_gpio_platform_data(gpio021_pins, ARRAY_SIZE(gpio021_pins),
				&gpio021_platform_data);
	init_gpio_platform_data(gpio022_pins, ARRAY_SIZE(gpio022_pins),
				&gpio022_platform_data);

	init_gpio_platform_data(gpio120_pins, ARRAY_SIZE(gpio120_pins),
				&gpio120_platform_data);
	init_gpio_platform_data(gpio121_pins, ARRAY_SIZE(gpio121_pins),
				&gpio121_platform_data);
	init_gpio_platform_data(gpio122_pins, ARRAY_SIZE(gpio122_pins),
				&gpio122_platform_data);

	init_gpio_platform_data(gpio220_pins, ARRAY_SIZE(gpio220_pins),
				&gpio220_platform_data);
	init_gpio_platform_data(gpio221_pins, ARRAY_SIZE(gpio221_pins),
				&gpio221_platform_data);
	init_gpio_platform_data(gpio222_pins, ARRAY_SIZE(gpio222_pins),
				&gpio222_platform_data);

	init_gpio_platform_data(gpio320_pins, ARRAY_SIZE(gpio320_pins),
				&gpio320_platform_data);
	init_gpio_platform_data(gpio321_pins, ARRAY_SIZE(gpio321_pins),
				&gpio321_platform_data);
	init_gpio_platform_data(gpio322_pins, ARRAY_SIZE(gpio322_pins),
				&gpio322_platform_data);

	init_gpio_platform_data(gpio420_pins, ARRAY_SIZE(gpio420_pins),
				&gpio420_platform_data);
	init_gpio_platform_data(gpio421_pins, ARRAY_SIZE(gpio421_pins),
				&gpio421_platform_data);
	init_gpio_platform_data(gpio422_pins, ARRAY_SIZE(gpio422_pins),
				&gpio422_platform_data);

	init_gpio_platform_data(gpio520_pins, ARRAY_SIZE(gpio520_pins),
				&gpio520_platform_data);
	init_gpio_platform_data(gpio521_pins, ARRAY_SIZE(gpio521_pins),
				&gpio521_platform_data);
	init_gpio_platform_data(gpio522_pins, ARRAY_SIZE(gpio522_pins),
				&gpio522_platform_data);

	init_gpio_platform_data(gpio620_pins, ARRAY_SIZE(gpio620_pins),
				&gpio620_platform_data);
	init_gpio_platform_data(gpio621_pins, ARRAY_SIZE(gpio621_pins),
				&gpio621_platform_data);
	init_gpio_platform_data(gpio622_pins, ARRAY_SIZE(gpio622_pins),
				&gpio622_platform_data);

	ret = init_i2c_devices();
	if (ret) {
		pr_err(DRIVER_NAME ": i2c subsystem initialization failed\n");
		return ret;
	}

	return 0;
}

static void i2c_exit(void)
{
	free_i2c_devices();
}

/* platform driver */

static int platform_probe(struct platform_device *dev)
{
	int ret;

	ret = i2c_init();
	if (ret) {
		dev_err(&dev->dev, "i2c initialization failed (%d)\n", ret);
		return ret;
	}

	ret = gpio_init();
	if (ret) {
		if (ret != -EPROBE_DEFER) {
			i2c_exit();
			dev_err(&dev->dev, "gpio initialization failed (%d)\n",
				ret);
		} else {
			dev_info(&dev->dev, "gpio initialization deferred\n");
		}
		return ret;
	}

	pr_info(DRIVER_NAME ": probe completed\n");
	return 0;
}

static int platform_remove(struct platform_device *dev)
{
	gpio_exit();
	i2c_exit();
	return 0;
}

static const struct platform_device_id platform_id[] = {
	{ DRIVER_NAME, 0 },
	{ }
};

MODULE_DEVICE_TABLE(platform, platform_id);

static struct platform_driver platform_driver = {
	.driver = {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
	},
	.probe = platform_probe,
	.remove = platform_remove,
	.id_table = platform_id
};

static struct platform_device *plat_device;

/* module init/exit */

static int __init platform_init(void)
{
	int ret = 0;

	/* register the platform driver */
	if (!driver_find(platform_driver.driver.name, &platform_bus_type)) {
		ret = platform_driver_register(&platform_driver);
		if (ret) {
			pr_err(DRIVER_NAME ": %s driver registration failed (%d)\n",
			       platform_driver.driver.name, ret);
			return ret;
		}
	}

	/* create the platform device */
	if (!plat_device) {
		plat_device = platform_device_register_simple(DRIVER_NAME, -1,
							      NULL, 0);
		if (IS_ERR(plat_device)) {
			ret = PTR_ERR(plat_device);
			plat_device = NULL;
			pr_err(DRIVER_NAME ": platform device registration failed (%d)\n",
			       ret);
			platform_driver_unregister(&platform_driver);
			return ret;
		}
	}

	pr_info(DRIVER_NAME ": version " DRIVER_VERSION " registered\n");
	return ret;
}

static void __exit platform_exit(void)
{
	platform_device_unregister(plat_device);
	plat_device = NULL;
	platform_driver_unregister(&platform_driver);
	pr_info(DRIVER_NAME ": driver unloaded\n");
}

module_init(platform_init);
module_exit(platform_exit);

MODULE_AUTHOR("David Yen (dhyen@cumulusnetworks.com)");
MODULE_DESCRIPTION("Lenovo NE2580 platform driver");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
