/*
 * accton_wedge100_platform.c - Accton Wedge100 Platform Support.
 *
 * Copyright (C) 2016 Cumulus Networks, Inc.
 * Author: Ellen Wang (ellen@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/err.h>
#include <linux/platform_device.h>
#include <linux/i2c/pca954x.h>
#include <linux/platform_data/pca953x.h>
#include <linux/platform_data/at24.h>
#include <linux/gpio.h>

#include "platform_defs.h"
#include "platform_bitfield.h"

#define DRIVER_NAME	"accton_wedge100_platform"
#define DRIVER_VERSION	"0.1"

/*
 * The platform has one CP2112 USB-to-I2C bridge, and possibly
 * other I2C buses that we don't care about.
 * The former has a CPLD and 5 pca9548 (8-port) I2C muxes
 * (OK they're really switches), 4 of which go to the 32 QSFP28
 * slots, and the last one goes to 6 pca9535 16-line IO expanders
 * and a 24c64 (64k-bit) EEPROM.
 *
 * Because the pca9548s are connected in parallel, and their
 * down-stream devices (the QSFP28s) all have the same address,
 * they need to disable all ports when not used (the deselect_on_exit
 * option).
 */

enum {
	W100_I2C_CP2112_BUS = -1,

	W100_I2C_MUX1_BUS0 = 10,
	W100_I2C_MUX1_BUS1,
	W100_I2C_MUX1_BUS2,
	W100_I2C_MUX1_BUS3,
	W100_I2C_MUX1_BUS4,
	W100_I2C_MUX1_BUS5,
	W100_I2C_MUX1_BUS6,
	W100_I2C_MUX1_BUS7,

	W100_I2C_MUX2_BUS0 = 20,
	W100_I2C_MUX2_BUS1,
	W100_I2C_MUX2_BUS2,
	W100_I2C_MUX2_BUS3,
	W100_I2C_MUX2_BUS4,
	W100_I2C_MUX2_BUS5,
	W100_I2C_MUX2_BUS6,
	W100_I2C_MUX2_BUS7,

	W100_I2C_MUX3_BUS0 = 30,
	W100_I2C_MUX3_BUS1,
	W100_I2C_MUX3_BUS2,
	W100_I2C_MUX3_BUS3,
	W100_I2C_MUX3_BUS4,
	W100_I2C_MUX3_BUS5,
	W100_I2C_MUX3_BUS6,
	W100_I2C_MUX3_BUS7,

	W100_I2C_MUX4_BUS0 = 40,
	W100_I2C_MUX4_BUS1,
	W100_I2C_MUX4_BUS2,
	W100_I2C_MUX4_BUS3,
	W100_I2C_MUX4_BUS4,
	W100_I2C_MUX4_BUS5,
	W100_I2C_MUX4_BUS6,
	W100_I2C_MUX4_BUS7,

	W100_I2C_MUX5_BUS0 = 50,
	W100_I2C_MUX5_BUS1,
	W100_I2C_MUX5_BUS2,
	W100_I2C_MUX5_BUS3,
	W100_I2C_MUX5_BUS4,
	W100_I2C_MUX5_BUS5,
	W100_I2C_MUX5_BUS6,
	W100_I2C_MUX5_BUS7,
};

/*
 * The list of I2C devices and their bus connections for this platform.
 *
 * Each entry is a bus number and a i2c_board_info.
 * The i2c_board_info specifies the device type, address,
 * and platform data depending on the device type.
 *
 * For muxes, we specify the bus number for each port,
 * and set the deselect_on_exit but (see comment above).
 *
 * For EEPROMs, including ones in the QSFP28 transceivers,
 * we specify the label, I2C address, size, and some flags.
 * All done in the magic 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.
 */

mk_pca9548(mux1, W100_I2C_MUX1_BUS0, 1)
mk_pca9548(mux2, W100_I2C_MUX2_BUS0, 1)
mk_pca9548(mux3, W100_I2C_MUX3_BUS0, 1)
mk_pca9548(mux4, W100_I2C_MUX4_BUS0, 1)
mk_pca9548(mux5, W100_I2C_MUX5_BUS0, 1)

mk_port_eeprom(port1,  50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port2,  50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port3,  50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port4,  50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port5,  50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port6,  50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port7,  50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port8,  50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port9,  50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port10, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port11, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port12, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port13, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port14, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port15, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port16, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port17, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port18, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port19, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port20, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port21, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port22, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port23, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port24, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port25, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port26, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port27, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port28, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port29, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port30, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port31, 50, 256, AT24_FLAG_IRUGO)
mk_port_eeprom(port32, 50, 256, AT24_FLAG_IRUGO)

mk_eeprom(board, 50, 8192, AT24_FLAG_ADDR16 | AT24_FLAG_IRUGO)

/*
 * GPIO definitions
 *
 * The GPIO infrastructure has an unfortunate interface for
 * initializing pins.  The pin names are specified in platform_data,
 * but everthing else has to be done by calls after pin creation.
 * So we consolidate all the information in one place (struct gpio_pin),
 * and try to make the best of it.  The pin name array is filled in
 * at runtime by init_gpio_platform_data(), before creating the I2C
 * device.  Then the pins are created by init_gpio_pins().
 */

struct gpio_pin {
	int num;
	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(_num, _name, _output, _active_low, _value) \
	{ \
		.num = (_num), \
		.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, \
	};

mk_gpio_pins(gpio1) = {
	mk_gpio_pin(0,  qsfp2_lp_mode,    1, 0, 0),
	mk_gpio_pin(1,  qsfp1_lp_mode,    1, 0, 0),
	mk_gpio_pin(2,  qsfp4_lp_mode,    1, 0, 0),
	mk_gpio_pin(3,  qsfp3_lp_mode,    1, 0, 0),
	mk_gpio_pin(4,  qsfp6_lp_mode,    1, 0, 0),
	mk_gpio_pin(5,  qsfp5_lp_mode,    1, 0, 0),
	mk_gpio_pin(6,  qsfp8_lp_mode,    1, 0, 0),
	mk_gpio_pin(7,  qsfp7_lp_mode,    1, 0, 0),
	mk_gpio_pin(8,  qsfp10_lp_mode,   1, 0, 0),
	mk_gpio_pin(9,  qsfp9_lp_mode,    1, 0, 0),
	mk_gpio_pin(10, qsfp12_lp_mode,   1, 0, 0),
	mk_gpio_pin(11, qsfp11_lp_mode,   1, 0, 0),
	mk_gpio_pin(12, qsfp14_lp_mode,   1, 0, 0),
	mk_gpio_pin(13, qsfp13_lp_mode,   1, 0, 0),
	mk_gpio_pin(14, qsfp16_lp_mode,   1, 0, 0),
	mk_gpio_pin(15, qsfp15_lp_mode,   1, 0, 0),
};

mk_gpio_pins(gpio2) = {
	mk_gpio_pin(0,  qsfp18_lp_mode,   1, 0, 0),
	mk_gpio_pin(1,  qsfp17_lp_mode,   1, 0, 0),
	mk_gpio_pin(2,  qsfp20_lp_mode,   1, 0, 0),
	mk_gpio_pin(3,  qsfp19_lp_mode,   1, 0, 0),
	mk_gpio_pin(4,  qsfp22_lp_mode,   1, 0, 0),
	mk_gpio_pin(5,  qsfp21_lp_mode,   1, 0, 0),
	mk_gpio_pin(6,  qsfp24_lp_mode,   1, 0, 0),
	mk_gpio_pin(7,  qsfp23_lp_mode,   1, 0, 0),
	mk_gpio_pin(8,  qsfp26_lp_mode,   1, 0, 0),
	mk_gpio_pin(9,  qsfp25_lp_mode,   1, 0, 0),
	mk_gpio_pin(10, qsfp28_lp_mode,   1, 0, 0),
	mk_gpio_pin(11, qsfp27_lp_mode,   1, 0, 0),
	mk_gpio_pin(12, qsfp30_lp_mode,   1, 0, 0),
	mk_gpio_pin(13, qsfp29_lp_mode,   1, 0, 0),
	mk_gpio_pin(14, qsfp32_lp_mode,   1, 0, 0),
	mk_gpio_pin(15, qsfp31_lp_mode,   1, 0, 0),
};

mk_gpio_pins(gpio3) = {
	mk_gpio_pin(0,  qsfp2_present,    0, 1, 0),
	mk_gpio_pin(1,  qsfp1_present,    0, 1, 0),
	mk_gpio_pin(2,  qsfp4_present,    0, 1, 0),
	mk_gpio_pin(3,  qsfp3_present,    0, 1, 0),
	mk_gpio_pin(4,  qsfp6_present,    0, 1, 0),
	mk_gpio_pin(5,  qsfp5_present,    0, 1, 0),
	mk_gpio_pin(6,  qsfp8_present,    0, 1, 0),
	mk_gpio_pin(7,  qsfp7_present,    0, 1, 0),
	mk_gpio_pin(8,  qsfp10_present,   0, 1, 0),
	mk_gpio_pin(9,  qsfp9_present,    0, 1, 0),
	mk_gpio_pin(10, qsfp12_present,   0, 1, 0),
	mk_gpio_pin(11, qsfp11_present,   0, 1, 0),
	mk_gpio_pin(12, qsfp14_present,   0, 1, 0),
	mk_gpio_pin(13, qsfp13_present,   0, 1, 0),
	mk_gpio_pin(14, qsfp16_present,   0, 1, 0),
	mk_gpio_pin(15, qsfp15_present,   0, 1, 0),
};

mk_gpio_pins(gpio4) = {
	mk_gpio_pin(0,  qsfp18_present,   0, 1, 0),
	mk_gpio_pin(1,  qsfp17_present,   0, 1, 0),
	mk_gpio_pin(2,  qsfp20_present,   0, 1, 0),
	mk_gpio_pin(3,  qsfp19_present,   0, 1, 0),
	mk_gpio_pin(4,  qsfp22_present,   0, 1, 0),
	mk_gpio_pin(5,  qsfp21_present,   0, 1, 0),
	mk_gpio_pin(6,  qsfp24_present,   0, 1, 0),
	mk_gpio_pin(7,  qsfp23_present,   0, 1, 0),
	mk_gpio_pin(8,  qsfp26_present,   0, 1, 0),
	mk_gpio_pin(9,  qsfp25_present,   0, 1, 0),
	mk_gpio_pin(10, qsfp28_present,   0, 1, 0),
	mk_gpio_pin(11, qsfp27_present,   0, 1, 0),
	mk_gpio_pin(12, qsfp30_present,   0, 1, 0),
	mk_gpio_pin(13, qsfp29_present,   0, 1, 0),
	mk_gpio_pin(14, qsfp32_present,   0, 1, 0),
	mk_gpio_pin(15, qsfp31_present,   0, 1, 0),
};

mk_gpio_pins(gpio5) = {
	mk_gpio_pin(0,  qsfp2_interrupt,  0, 1, 0),
	mk_gpio_pin(1,  qsfp1_interrupt,  0, 1, 0),
	mk_gpio_pin(2,  qsfp4_interrupt,  0, 1, 0),
	mk_gpio_pin(3,  qsfp3_interrupt,  0, 1, 0),
	mk_gpio_pin(4,  qsfp6_interrupt,  0, 1, 0),
	mk_gpio_pin(5,  qsfp5_interrupt,  0, 1, 0),
	mk_gpio_pin(6,  qsfp8_interrupt,  0, 1, 0),
	mk_gpio_pin(7,  qsfp7_interrupt,  0, 1, 0),
	mk_gpio_pin(8,  qsfp10_interrupt, 0, 1, 0),
	mk_gpio_pin(9,  qsfp9_interrupt,  0, 1, 0),
	mk_gpio_pin(10, qsfp12_interrupt, 0, 1, 0),
	mk_gpio_pin(11, qsfp11_interrupt, 0, 1, 0),
	mk_gpio_pin(12, qsfp14_interrupt, 0, 1, 0),
	mk_gpio_pin(13, qsfp13_interrupt, 0, 1, 0),
	mk_gpio_pin(14, qsfp16_interrupt, 0, 1, 0),
	mk_gpio_pin(15, qsfp15_interrupt, 0, 1, 0),
};

mk_gpio_pins(gpio6) = {
	mk_gpio_pin(0,  qsfp18_interrupt, 0, 1, 0),
	mk_gpio_pin(1,  qsfp17_interrupt, 0, 1, 0),
	mk_gpio_pin(2,  qsfp20_interrupt, 0, 1, 0),
	mk_gpio_pin(3,  qsfp19_interrupt, 0, 1, 0),
	mk_gpio_pin(4,  qsfp22_interrupt, 0, 1, 0),
	mk_gpio_pin(5,  qsfp21_interrupt, 0, 1, 0),
	mk_gpio_pin(6,  qsfp24_interrupt, 0, 1, 0),
	mk_gpio_pin(7,  qsfp23_interrupt, 0, 1, 0),
	mk_gpio_pin(8,  qsfp26_interrupt, 0, 1, 0),
	mk_gpio_pin(9,  qsfp25_interrupt, 0, 1, 0),
	mk_gpio_pin(10, qsfp28_interrupt, 0, 1, 0),
	mk_gpio_pin(11, qsfp27_interrupt, 0, 1, 0),
	mk_gpio_pin(12, qsfp30_interrupt, 0, 1, 0),
	mk_gpio_pin(13, qsfp29_interrupt, 0, 1, 0),
	mk_gpio_pin(14, qsfp32_interrupt, 0, 1, 0),
	mk_gpio_pin(15, qsfp31_interrupt, 0, 1, 0),
};

mk_gpio_platform_data(gpio1, 1, 16)
mk_gpio_platform_data(gpio2, 17, 16)
mk_gpio_platform_data(gpio3, 101, 16)
mk_gpio_platform_data(gpio4, 117, 16)
mk_gpio_platform_data(gpio5, 201, 16)
mk_gpio_platform_data(gpio6, 217, 16)

static struct platform_i2c_device_info i2c_devices[] = {
	/* devices on CP2112 bus */
	mk_i2cdev(W100_I2C_CP2112_BUS, "pca9548", 0x70, &mux1_platform_data),
	mk_i2cdev(W100_I2C_CP2112_BUS, "pca9548", 0x71, &mux2_platform_data),
	mk_i2cdev(W100_I2C_CP2112_BUS, "pca9548", 0x72, &mux3_platform_data),
	mk_i2cdev(W100_I2C_CP2112_BUS, "pca9548", 0x73, &mux4_platform_data),
	mk_i2cdev(W100_I2C_CP2112_BUS, "pca9548", 0x74, &mux5_platform_data),
	mk_i2cdev(W100_I2C_CP2112_BUS, "act_wedge100_cpld", 0x32, NULL),

	/*
	 * devices on pca9548 5
	 *
	 * Do this one first so the main EEPROM is eeprom0, and
	 * port EEPROMs are numbered exactly like the ports.
	 * Not required, just nice.
	 */
	mk_i2cdev(W100_I2C_MUX5_BUS0, "pca9535", 0x20, &gpio1_platform_data),
	mk_i2cdev(W100_I2C_MUX5_BUS1, "pca9535", 0x21, &gpio2_platform_data),
	mk_i2cdev(W100_I2C_MUX5_BUS2, "pca9535", 0x22, &gpio3_platform_data),
	mk_i2cdev(W100_I2C_MUX5_BUS3, "pca9535", 0x23, &gpio4_platform_data),
	mk_i2cdev(W100_I2C_MUX5_BUS4, "pca9535", 0x24, &gpio5_platform_data),
	mk_i2cdev(W100_I2C_MUX5_BUS5, "pca9535", 0x25, &gpio6_platform_data),
	mk_i2cdev(W100_I2C_MUX5_BUS6, "24c64",   0x50, &board_50_at24),

	/* devices on pca9548 1 */
	mk_i2cdev(W100_I2C_MUX1_BUS1, "24c02", 0x50, &port1_50_at24),
	mk_i2cdev(W100_I2C_MUX1_BUS0, "24c02", 0x50, &port2_50_at24),
	mk_i2cdev(W100_I2C_MUX1_BUS3, "24c02", 0x50, &port3_50_at24),
	mk_i2cdev(W100_I2C_MUX1_BUS2, "24c02", 0x50, &port4_50_at24),
	mk_i2cdev(W100_I2C_MUX1_BUS5, "24c02", 0x50, &port5_50_at24),
	mk_i2cdev(W100_I2C_MUX1_BUS4, "24c02", 0x50, &port6_50_at24),
	mk_i2cdev(W100_I2C_MUX1_BUS7, "24c02", 0x50, &port7_50_at24),
	mk_i2cdev(W100_I2C_MUX1_BUS6, "24c02", 0x50, &port8_50_at24),

	/* devices on pca9548 2 */
	mk_i2cdev(W100_I2C_MUX2_BUS1, "24c02", 0x50, &port9_50_at24),
	mk_i2cdev(W100_I2C_MUX2_BUS0, "24c02", 0x50, &port10_50_at24),
	mk_i2cdev(W100_I2C_MUX2_BUS3, "24c02", 0x50, &port11_50_at24),
	mk_i2cdev(W100_I2C_MUX2_BUS2, "24c02", 0x50, &port12_50_at24),
	mk_i2cdev(W100_I2C_MUX2_BUS5, "24c02", 0x50, &port13_50_at24),
	mk_i2cdev(W100_I2C_MUX2_BUS4, "24c02", 0x50, &port14_50_at24),
	mk_i2cdev(W100_I2C_MUX2_BUS7, "24c02", 0x50, &port15_50_at24),
	mk_i2cdev(W100_I2C_MUX2_BUS6, "24c02", 0x50, &port16_50_at24),

	/* devices on pca9548 3 */
	mk_i2cdev(W100_I2C_MUX3_BUS1, "24c02", 0x50, &port17_50_at24),
	mk_i2cdev(W100_I2C_MUX3_BUS0, "24c02", 0x50, &port18_50_at24),
	mk_i2cdev(W100_I2C_MUX3_BUS3, "24c02", 0x50, &port19_50_at24),
	mk_i2cdev(W100_I2C_MUX3_BUS2, "24c02", 0x50, &port20_50_at24),
	mk_i2cdev(W100_I2C_MUX3_BUS5, "24c02", 0x50, &port21_50_at24),
	mk_i2cdev(W100_I2C_MUX3_BUS4, "24c02", 0x50, &port22_50_at24),
	mk_i2cdev(W100_I2C_MUX3_BUS7, "24c02", 0x50, &port23_50_at24),
	mk_i2cdev(W100_I2C_MUX3_BUS6, "24c02", 0x50, &port24_50_at24),

	/* devices on pca9548 4 */
	mk_i2cdev(W100_I2C_MUX4_BUS1, "24c02", 0x50, &port25_50_at24),
	mk_i2cdev(W100_I2C_MUX4_BUS0, "24c02", 0x50, &port26_50_at24),
	mk_i2cdev(W100_I2C_MUX4_BUS3, "24c02", 0x50, &port27_50_at24),
	mk_i2cdev(W100_I2C_MUX4_BUS2, "24c02", 0x50, &port28_50_at24),
	mk_i2cdev(W100_I2C_MUX4_BUS5, "24c02", 0x50, &port29_50_at24),
	mk_i2cdev(W100_I2C_MUX4_BUS4, "24c02", 0x50, &port30_50_at24),
	mk_i2cdev(W100_I2C_MUX4_BUS7, "24c02", 0x50, &port31_50_at24),
	mk_i2cdev(W100_I2C_MUX4_BUS6, "24c02", 0x50, &port32_50_at24),
};

/*
 * Utility functions for I2C
 */

static struct i2c_adapter *get_adapter(int bus)
{
	int bail;
	struct i2c_adapter *adapter;

	for (bail = 10; --bail >= 0;) {
		adapter = i2c_get_adapter(bus);
		if (adapter)
			return adapter;
		msleep(100);
	}
	return NULL;
}

static int get_bus_by_name(char *name)
{
	struct i2c_adapter *adapter;
	int i;

	for (i = 0; i < W100_I2C_MUX1_BUS0; i++) {
		adapter = get_adapter(i);
		if (adapter) {
			if (strncmp(adapter->name, name, strlen(name)) == 0) {
				i2c_put_adapter(adapter);
				return i;
			}
			i2c_put_adapter(adapter);
		}
	}
	return -1;
}

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

	adapter = get_adapter(bus);
	if (!adapter) {
		pr_err("could not get I2C adapter %d\n", bus);
		client = ERR_PTR(-ENODEV);
		goto exit;
	}
	client = i2c_new_device(adapter, board_info);
	if (!client) {
		pr_err("could not add I2C device at bus %d type %s addr %#x\n",
		       bus, board_info->type, board_info->addr);
		client = ERR_PTR(-ENODEV);
	}
	i2c_put_adapter(adapter);
exit:
	return client;
}

static int init_i2c_devices(void)
{
	int cp2112_bus;
	int i;
	int ret;

	cp2112_bus = get_bus_by_name("CP2112 SMBus Bridge");
	if (cp2112_bus < 0) {
		pr_err("could not find CP2112 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;

		if (bus == W100_I2C_CP2112_BUS)
			bus = cp2112_bus;
		client = add_i2c_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:
	return ret;
}

static void free_i2c_devices(void)
{
	int i;

	for (i = ARRAY_SIZE(i2c_devices); --i >= 0;) {
		struct i2c_client *c = i2c_devices[i].client;

		if (c) {
			i2c_devices[i].client = NULL;
			i2c_unregister_device(c);
		}
	}
}

/*
 * Utility functions for GPIO
 */

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].num] = pins[i].name;
		pins[i].num += pdata->gpio_base;
	}
}

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

	for (i = 0; i < num_pins; i++) {
		struct gpio_pin *pin = pins + i;
		int bail;

		/* try a few times because probe happens asynchronously */
		for (bail = 10; --bail >= 0;) {
			ret = gpio_request(pin->num, NULL);
			if (!ret)
				break;
			pr_err("failed to request GPIO pin %s (%d), err %d\n",
			       pin->name, pin->num, ret);
			if (ret != -EPROBE_DEFER)
				break;
			msleep(100);
		}
		if (ret)
			return ret;
		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;
		}
	}
	return 0;
}

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;
		}
}

/*
 * Module init and exit for all I2C devices, including GPIO
 */

static void free_all(void)
{
	free_gpio_pins(gpio6_pins, ARRAY_SIZE(gpio6_pins));
	free_gpio_pins(gpio5_pins, ARRAY_SIZE(gpio5_pins));
	free_gpio_pins(gpio4_pins, ARRAY_SIZE(gpio4_pins));
	free_gpio_pins(gpio3_pins, ARRAY_SIZE(gpio3_pins));
	free_gpio_pins(gpio2_pins, ARRAY_SIZE(gpio2_pins));
	free_gpio_pins(gpio1_pins, ARRAY_SIZE(gpio1_pins));
	free_i2c_devices();
}

static int __init accton_wedge100_i2c_init(void)
{
	int ret;

	init_gpio_platform_data(gpio1_pins, ARRAY_SIZE(gpio1_pins),
				&gpio1_platform_data);
	init_gpio_platform_data(gpio2_pins, ARRAY_SIZE(gpio2_pins),
				&gpio2_platform_data);
	init_gpio_platform_data(gpio3_pins, ARRAY_SIZE(gpio3_pins),
				&gpio3_platform_data);
	init_gpio_platform_data(gpio4_pins, ARRAY_SIZE(gpio4_pins),
				&gpio4_platform_data);
	init_gpio_platform_data(gpio5_pins, ARRAY_SIZE(gpio5_pins),
				&gpio5_platform_data);
	init_gpio_platform_data(gpio6_pins, ARRAY_SIZE(gpio6_pins),
				&gpio6_platform_data);

	ret = init_i2c_devices();
	if (ret)
		goto err_exit;

	ret = init_gpio_pins(gpio1_pins, ARRAY_SIZE(gpio1_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio2_pins, ARRAY_SIZE(gpio2_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio3_pins, ARRAY_SIZE(gpio3_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio4_pins, ARRAY_SIZE(gpio4_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio5_pins, ARRAY_SIZE(gpio5_pins));
	if (ret)
		goto err_exit;
	ret = init_gpio_pins(gpio6_pins, ARRAY_SIZE(gpio6_pins));
	if (ret)
		goto err_exit;

	return 0;

err_exit:
	free_all();
	return ret;
}

static void __exit accton_wedge100_i2c_exit(void)
{
	free_all();
}

/*
 * CPLD driver
 */

#define CPLD_NREGS 0x40

#define cpld_read_reg bf_i2c_read_reg
#define cpld_write_reg bf_i2c_write_reg

/*
 * CPLD register definitions
 * Only the useful ones are exposed.
 */

static const char * const cpld_board_model_values[] = {
	"wdege100",
	"6-pack line card",
	"6-pack fabric card",
	"reserved",
};

mk_bf_ro(cpld, board_revision,  0x00, 0, 4, NULL, BF_DECIMAL)
mk_bf_ro(cpld, board_model,     0x00, 4, 2, cpld_board_model_values, 0)

mk_bf_ro(cpld, cpld_revision,   0x01, 0, 6, NULL, BF_DECIMAL)
mk_bf_ro(cpld, cpld_released,   0x01, 6, 1, NULL, 0)

mk_bf_ro(cpld, cpld_subversion, 0x02, 0, 8, NULL, BF_DECIMAL)

static const char * const cpld_rov_values[] = {
	"1.2000",
	"1.1750",
	"1.1500",
	"1.2500",
	"1.1000",
	"1.0750",
	"1.0500",
	"1.0250",
	"1.0000",
	"0.9250",
	"0.9500",
	"0.9250",
	"0.9000",
	"0.8750",
	"0.8500",
	"0.8250",
};

mk_bf_ro(cpld, rov, 0x0b, 0, 4, cpld_rov_values, 0)

mk_bf_ro(cpld, reset_reason,         0x0d, 0, 8, NULL, 0)
mk_bf_ro(cpld, reset_reason_source1, 0x0e, 0, 8, NULL, 0)
mk_bf_ro(cpld, reset_reason_source2, 0x0f, 0, 8, NULL, 0)

static const char * const cpld_led_button_values[] = {
	"top-bottom",
	"bottom",
	"top",
	"none",
};

mk_bf_ro(cpld, led_button, 0x1b, 0, 2, cpld_led_button_values, 0)

mk_bf_ro(cpld, psu_pwr1_present, 0x10, 0, 1, NULL, BF_COMPLEMENT)
mk_bf_ro(cpld, psu_pwr1_dc_ok,   0x10, 1, 1, NULL, 0)
mk_bf_ro(cpld, psu_pwr1_ac_ok,   0x10, 2, 1, NULL, 0)
mk_bf_ro(cpld, psu_pwr1_alarm,   0x10, 3, 1, NULL, BF_COMPLEMENT)
mk_bf_ro(cpld, psu_pwr2_present, 0x10, 4, 1, NULL, BF_COMPLEMENT)
mk_bf_ro(cpld, psu_pwr2_dc_ok,   0x10, 5, 1, NULL, 0)
mk_bf_ro(cpld, psu_pwr2_ac_ok,   0x10, 6, 1, NULL, 0)
mk_bf_ro(cpld, psu_pwr2_alarm,   0x10, 7, 1, NULL, BF_COMPLEMENT)

mk_bf_rw(cpld, qsfp_reset, 0x34, 0, 32, NULL, BF_COMPLEMENT)
mk_bf_rw(cpld, cp2112_reset, 0x39, 1, 1, NULL, 0)

mk_bf_rw(cpld, led_bcm_reset,         0x3c, 0, 1, NULL, 0)
mk_bf_rw(cpld, led_bcm_enable,        0x3c, 1, 1, NULL, 0)
mk_bf_rw(cpld, led_act_enable,         0x3c, 2, 1, NULL, 0)
mk_bf_rw(cpld, led_walk_test_enable,  0x3c, 3, 1, NULL, 0)
mk_bf_rw(cpld, led_test_mode,         0x3c, 4, 2, NULL, 0)
mk_bf_rw(cpld, led_test_blink_enable, 0x3c, 6, 1, NULL, 0)
mk_bf_rw(cpld, led_test_mode_enable,  0x3c, 7, 1, NULL, 0)

mk_bf_rw(cpld, led_test_num,          0x3d, 0, 5, NULL, 0)
mk_bf_rw(cpld, led_test_green,        0x3d, 5, 1, NULL, 0)
mk_bf_rw(cpld, led_test_blue,         0x3d, 6, 1, NULL, 0)
mk_bf_rw(cpld, led_test_red,          0x3d, 7, 1, NULL, 0)

static const char * const cpld_led_system_values[] = {
	PLATFORM_LED_OFF,
	PLATFORM_LED_RED,
	PLATFORM_LED_GREEN,
	PLATFORM_LED_YELLOW,
	PLATFORM_LED_BLUE,
	"magenta",
	"cyan",
	"white",
	PLATFORM_LED_OFF,
	PLATFORM_LED_RED_BLINKING,
	PLATFORM_LED_GREEN_BLINKING,
	PLATFORM_LED_YELLOW_BLINKING,
	PLATFORM_LED_BLUE_BLINKING,
	"magenta_blinking",
	"cyan_blinking",
	"white",
};

mk_bf_rw(cpld, led_system1, 0x3e, 0, 4, cpld_led_system_values, 0)
mk_bf_rw(cpld, led_system2, 0x3f, 0, 4, cpld_led_system_values, 0)

struct attribute *cpld_attrs[] = {
	&cpld_board_revision.attr,
	&cpld_board_model.attr,
	&cpld_cpld_revision.attr,
	&cpld_cpld_released.attr,
	&cpld_cpld_subversion.attr,
	&cpld_rov.attr,
	&cpld_reset_reason.attr,
	&cpld_reset_reason_source1.attr,
	&cpld_reset_reason_source2.attr,
	&cpld_led_button.attr,
	&cpld_psu_pwr1_present.attr,
	&cpld_psu_pwr1_dc_ok.attr,
	&cpld_psu_pwr1_ac_ok.attr,
	&cpld_psu_pwr1_alarm.attr,
	&cpld_psu_pwr2_present.attr,
	&cpld_psu_pwr2_dc_ok.attr,
	&cpld_psu_pwr2_ac_ok.attr,
	&cpld_psu_pwr2_alarm.attr,
	&cpld_led_bcm_reset.attr,
	&cpld_led_bcm_enable.attr,
	&cpld_led_act_enable.attr,
	&cpld_led_walk_test_enable.attr,
	&cpld_led_test_mode.attr,
	&cpld_led_test_blink_enable.attr,
	&cpld_led_test_mode_enable.attr,
	&cpld_led_test_num.attr,
	&cpld_led_test_green.attr,
	&cpld_led_test_blue.attr,
	&cpld_led_test_red.attr,
	&cpld_led_system1.attr,
	&cpld_led_system2.attr,
	&cpld_qsfp_reset.attr,
	&cpld_cp2112_reset.attr,
	NULL
};

static struct attribute_group cpld_attr_group = {
	.attrs = cpld_attrs,
};

static int cpld_probe(struct i2c_client *client,
		      const struct i2c_device_id *id)
{
	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
	int boardrev;
	int cpldrev;
	int ret;

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
		dev_err(&client->dev,
			"adapter does not support I2C_FUNC_SMBUS_BYTE_DATA\n");
		ret = -EINVAL;
		goto err;
	}

	/*
	 * Probe the hardware by reading the revision numbers.
	 */
	ret = i2c_smbus_read_byte_data(client, 0);
	if (ret < 0) {
		dev_err(&client->dev,
			"read board revision register error %d\n", ret);
		goto err;
	}
	boardrev = ret;
	ret = i2c_smbus_read_byte_data(client, 1);
	if (ret < 0) {
		dev_err(&client->dev,
			"read CPLD revision register error %d\n", ret);
		goto err;
	}
	cpldrev = ret;

	/*
	 * Create sysfs nodes.
	 */
	ret = sysfs_create_group(&client->dev.kobj, &cpld_attr_group);
	if (ret) {
		dev_err(&client->dev, "sysfs_create_group failed\n");
		goto err;
	}

	/*
	 * All clear from this point on
	 */
	dev_info(&client->dev,
		 "device created, board model %d rev %d, CPLD rev %d %s\n",
		 (boardrev & 0x30) >> 4,
		 boardrev & 0x0f,
		 cpldrev & 0x3f,
		 cpldrev & 0x40 ? "released" : "unreleased");

#ifdef DEBUG
	for (i = 0; i < CPLD_NREGS; i++) {
		ret = i2c_smbus_read_byte_data(client, i);
		dev_dbg(&client->dev,
			ret < 0 ? "CPLD[%d] read error %d\n" :
				  "CPLD[%d] %#04x\n",
			i, ret);
	}
#endif

	return 0;

err:
	return ret;
}

static int cpld_remove(struct i2c_client *client)
{
	dev_info(&client->dev, "device removed\n");
	return 0;
}

static const struct i2c_device_id cpld_id[] = {
	{ "act_wedge100_cpld", 0 },	/* full name is too long, ick */
	{ }
};
MODULE_DEVICE_TABLE(i2c, cpld_id);

static struct i2c_driver cpld_driver = {
	.driver = {
		.name = "accton_wedge100_cpld",
		.owner = THIS_MODULE,
	},
	.probe = cpld_probe,
	.remove = cpld_remove,
	.id_table = cpld_id,
};

static int __init accton_wedge100_cpld_init(void)
{
	pr_info(DRIVER_NAME " loading CPLD driver\n");
	return i2c_add_driver(&cpld_driver);
}

static void __exit accton_wedge100_cpld_exit(void)
{
	i2c_del_driver(&cpld_driver);
	pr_info(DRIVER_NAME " CPLD driver unloaded\n");
}

/*
 * Module init and exit
 */

static int __init accton_wedge100_init(void)
{
	int ret;

	/*
	 * Initialize the CPLD driver first, because it's an I2C
	 * device and will get instantiated along with the other
	 * I2C devices in accton_wedge100_i2c_init().
	 */
	ret = accton_wedge100_cpld_init();
	if (ret) {
		pr_err("CPLD initialization failed\n");
		return ret;
	}

	ret = accton_wedge100_i2c_init();
	if (ret) {
		pr_err("I2C initialization failed\n");
		return ret;
	}

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

static void __exit accton_wedge100_exit(void)
{
	accton_wedge100_i2c_exit();
	accton_wedge100_cpld_exit();
	pr_info(DRIVER_NAME " driver unloaded\n");
}

module_init(accton_wedge100_init);
module_exit(accton_wedge100_exit);

MODULE_AUTHOR("Ellen Wang (ellen@cumulusnetworks.com)");
MODULE_DESCRIPTION("Accton Wedge100 Platform Support");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
