 /*
 * qct_ly4r_platform.c - Quanta LY4R Platform Support.
 *
 * Copyright (C) 2017 Cumulus Networks, Inc. all rights reserved
 *
 * 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; version 2.
 *
 * 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.
 *
 * https://www.gnu.org/licenses/gpl-2.0-standalone.html
 */

#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/i2c-mux.h>
#include <linux/i2c/sff-8436.h>
#include <linux/i2c/pca954x.h>
#include <linux/i2c/pmbus.h>
#include <linux/platform_data/at24.h>
#include <linux/platform_data/pca953x.h>
#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>

#include "platform_defs.h"

#define DRIVER_NAME	"qct_ly4r_platform"
#define DRIVER_VERSION	"0.1"

/*---------------------------------------------------------------------
 *
 * Platform driver
 *
 *-------------------------------------------------------------------*/

/*
 * The platform has 2 types of i2c SMBUSes, i801 (Intel 82801
 * (ICH/PCH)) and ISMT (Intel SMBus Message Transport). 
 */

/* i2c bus adapter numbers for the i2c busses */
enum {
	CL_I2C_I801_BUS = 0,
	CL_I2C_ISMT_BUS,
	CL_I2C_MUX1_BUS0=10,
	CL_I2C_MUX1_BUS1=11,
	CL_I2C_MUX1_BUS2=12,
	CL_I2C_MUX1_BUS3=13,
	CL_I2C_MUX1_BUS4=14,
	CL_I2C_MUX1_BUS5=15,
	CL_I2C_MUX1_BUS6=16,
	CL_I2C_MUX1_BUS7=17,
	CL_I2C_MUX2=100,
};

mk_eeprom(board, 54, 256, AT24_FLAG_IRUGO);

struct cl_platform_device_info {
	int bus;
	struct i2c_board_info board_info;
};

struct port_info {
	struct i2c_board_info *b;
	struct i2c_client *c;
};

/*
 * platform data for the gpio chip
 */
#define QCT_LY4R_GPIO_PIN_COUNT          40
#define QCT_LY4R_GPIO_BASE               412
#define QCT_LY4R_GPIO_SFP_1_TX_FAULT_L   (QCT_LY4R_GPIO_BASE)
#define QCT_LY4R_GPIO_SFP_1_TX_DISABLE_L (QCT_LY4R_GPIO_BASE+1)
#define QCT_LY4R_GPIO_SFP_1_PRESENT_L    (QCT_LY4R_GPIO_BASE+2)
#define QCT_LY4R_GPIO_SFP_1_RX_LOSS_L    (QCT_LY4R_GPIO_BASE+3)
#define QCT_LY4R_GPIO_SFP_2_TX_FAULT_L   (QCT_LY4R_GPIO_BASE+4)
#define QCT_LY4R_GPIO_SFP_2_TX_DISABLE_L (QCT_LY4R_GPIO_BASE+5)
#define QCT_LY4R_GPIO_SFP_2_PRESENT_L    (QCT_LY4R_GPIO_BASE+6)
#define QCT_LY4R_GPIO_SFP_2_RX_LOSS_L    (QCT_LY4R_GPIO_BASE+7)
#define QCT_LY4R_GPIO_SFP_3_TX_FAULT_L   (QCT_LY4R_GPIO_BASE+8)
#define QCT_LY4R_GPIO_SFP_3_TX_DISABLE_L (QCT_LY4R_GPIO_BASE+9)
#define QCT_LY4R_GPIO_SFP_3_PRESENT_L    (QCT_LY4R_GPIO_BASE+10)
#define QCT_LY4R_GPIO_SFP_3_RX_LOSS_L    (QCT_LY4R_GPIO_BASE+11)
#define QCT_LY4R_GPIO_SFP_4_TX_FAULT_L   (QCT_LY4R_GPIO_BASE+12)
#define QCT_LY4R_GPIO_SFP_4_TX_DISABLE_L (QCT_LY4R_GPIO_BASE+13)
#define QCT_LY4R_GPIO_SFP_4_PRESENT_L    (QCT_LY4R_GPIO_BASE+14)
#define QCT_LY4R_GPIO_SFP_4_RX_LOSS_L    (QCT_LY4R_GPIO_BASE+15)
#define QCT_LY4R_GPIO_SFP_M_TX_FAULT_L   (QCT_LY4R_GPIO_BASE+16)
#define QCT_LY4R_GPIO_SFP_M_TX_DISABLE_L (QCT_LY4R_GPIO_BASE+17)
#define QCT_LY4R_GPIO_SFP_M_PRESENT_L    (QCT_LY4R_GPIO_BASE+18)
#define QCT_LY4R_GPIO_SFP_M_RX_LOSS_L    (QCT_LY4R_GPIO_BASE+19)
#define QCT_LY4R_GPIO_LED_BOOT           (QCT_LY4R_GPIO_BASE+20)
#define QCT_LY4R_GPIO_LED_STATUS         (QCT_LY4R_GPIO_BASE+21)
#define QCT_LY4R_GPIO_SFP_POWER_ENABLE_L (QCT_LY4R_GPIO_BASE+22)

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(qct_ly4r_gpio) = {
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_1_TX_FAULT_L,   sfp49_tx_fault,   0, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_1_TX_DISABLE_L, sfp49_tx_disable, 1, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_1_PRESENT_L,    sfp49_present,    0, 1, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_1_RX_LOSS_L,    sfp49_rx_los,     0, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_2_TX_FAULT_L,   sfp50_tx_fault,   0, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_2_TX_DISABLE_L, sfp50_tx_disable, 1, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_2_PRESENT_L,    sfp50_present,    0, 1, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_2_RX_LOSS_L,    sfp50_rx_los,     0, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_3_TX_FAULT_L,   sfp51_tx_fault,   0, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_3_TX_DISABLE_L, sfp51_tx_disable, 1, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_3_PRESENT_L,    sfp51_present,    0, 1, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_3_RX_LOSS_L,    sfp51_rx_los,     0, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_4_TX_FAULT_L,   sfp52_tx_fault,   0, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_4_TX_DISABLE_L, sfp52_tx_disable, 1, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_4_PRESENT_L,    sfp52_present,    0, 1, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_4_RX_LOSS_L,    sfp52_rx_los,     0, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_LED_BOOT,           led_boot,         1, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_LED_STATUS,         led_status,       1, 0, 0),
	mk_gpio_pin(QCT_LY4R_GPIO_SFP_POWER_ENABLE_L, sfp_power_enable, 1, 1, 0),
};
static const char *pca953x_gpio_names[QCT_LY4R_GPIO_PIN_COUNT];

static struct pca953x_platform_data gpio_platform_data = {
	.gpio_base = QCT_LY4R_GPIO_BASE,
	.names = pca953x_gpio_names,
};

static int num_gpio_pins = ARRAY_SIZE(qct_ly4r_gpio_pins);
static int claimed_gpio_pins[ARRAY_SIZE(qct_ly4r_gpio_pins)];

mk_port_eeprom(port49,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port50,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port51,  50, 512, AT24_FLAG_IRUGO)
mk_port_eeprom(port52,  50, 512, AT24_FLAG_IRUGO)

/*
 * PCA9548 at iSMT 0x73
 */
static struct pca954x_platform_mode mux1_platform_modes[] = {
	{
		.adap_id = CL_I2C_MUX1_BUS0, .deselect_on_exit = 1,
	},
	{
		.adap_id = CL_I2C_MUX1_BUS1, .deselect_on_exit = 1,
	},
	{
		.adap_id = CL_I2C_MUX1_BUS2, .deselect_on_exit = 1,
	},
	{
		.adap_id = CL_I2C_MUX1_BUS3, .deselect_on_exit = 1,
	},
	{
		.adap_id = CL_I2C_MUX1_BUS4, .deselect_on_exit = 1,
	},
	{
		.adap_id = CL_I2C_MUX1_BUS5, .deselect_on_exit = 1,
	},
	{
		.adap_id = CL_I2C_MUX1_BUS6, .deselect_on_exit = 1,
	},
	{
		.adap_id = CL_I2C_MUX1_BUS7, .deselect_on_exit = 1,
	},
};

/*
 *
*/
static struct pca954x_platform_data mux1_platform_data = {
	.modes = mux1_platform_modes,
	.num_modes = ARRAY_SIZE(mux1_platform_modes),
};


static struct cl_platform_device_info i2c_devices[] = {
	mk_i2cdev(CL_I2C_ISMT_BUS,  "24c02",   0x54, &board_54_at24),
	mk_i2cdev(CL_I2C_ISMT_BUS,  "pca9548", 0x73, &mux1_platform_data),
	mk_i2cdev(CL_I2C_ISMT_BUS,  "pca9698", 0x20, &gpio_platform_data),
	mk_i2cdev(CL_I2C_MUX1_BUS0, "24c02",   0x50, &port49_50_at24),
	mk_i2cdev(CL_I2C_MUX1_BUS1, "24c02",   0x50, &port50_50_at24),
	mk_i2cdev(CL_I2C_MUX1_BUS2, "24c02",   0x50, &port51_50_at24),
	mk_i2cdev(CL_I2C_MUX1_BUS3, "24c02",   0x50, &port52_50_at24),
};

/**
 * Array of allocated i2c_client objects.  Need to track these in
 * order to free them later.
 *
 */
struct i2c_client_info {
	struct i2c_client *i2c_client;
	struct cl_platform_device_info *platform_info;
};

static struct i2c_client_info i2c_clients[ARRAY_SIZE(i2c_devices)];
static int num_i2c_clients;
/**
 *
 * TODO:  This should be common code used by most platforms....
 *
 * Fetch i2c adapter by bus number.
 *
 * The retry / sleep logic is required as the dynamically allocated
 * adapters from the MUX devices can take "some time" to become
 * available.
 */
static struct i2c_adapter *get_adapter(int bus)
{
	int bail = 100;
	struct i2c_adapter *adapter;

	for (; bail; bail--) {
		adapter = i2c_get_adapter(bus);
		if (adapter)
			return adapter;
		msleep(10.5);
	}
	return NULL;
}

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 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 void free_i2c_data(void)
{
	int i;
	/*
	 * Free the devices in reverse order so that child devices are
	 * freed before parent mux devices.
	 */
	for (i = num_i2c_clients - 1; i >= 0; i--)
		i2c_unregister_device(i2c_clients[i].i2c_client);
}

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

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

static int ismt_bus_num;
static int i801_bus_num;

/**
 * qct_ly4r_i2c_init -- Initialize I2C devices
 *
 */
static int __init qct_ly4r_i2c_init(void)
{
	struct i2c_client *client;
	int i;
	int ret = -1;

	ismt_bus_num = get_bus_by_name(ISMT_ADAPTER_NAME);
	if (ismt_bus_num < 0) {
		pr_err("could not find ismt adapter bus\n");
		ret = -ENODEV;
		goto err_exit;
	}
	i801_bus_num = get_bus_by_name(I801_ADAPTER_NAME);
	if (i801_bus_num < 0) {
		pr_err("could not find I801 adapter bus\n");
		ret = -ENODEV;
		goto err_exit;
	}

	for (i = 0; i < ARRAY_SIZE(i2c_devices); i++) {
		/*
		 * Map logical buses CL_I2C_ISMT_BUS and
		 * CL_I2C_I801_BUS to their dynamically discovered
		 * bus numbers.
		 */
		switch (i2c_devices[i].bus) {
		case CL_I2C_ISMT_BUS:
			i2c_devices[i].bus = ismt_bus_num;
			break;
		case CL_I2C_I801_BUS:
			i2c_devices[i].bus = i801_bus_num;
			break;
		default:
			break;
			/* Fall through for PCA9548 buses */
		};
		client = add_i2c_client(i2c_devices[i].bus,
					&i2c_devices[i].board_info);
		if (IS_ERR(client)) {
			ret = PTR_ERR(client);
			goto err_exit;
		}
		i2c_clients[num_i2c_clients].platform_info = &i2c_devices[i];
		i2c_clients[num_i2c_clients++].i2c_client = client;
	}
err_exit:
	return 0;

	free_i2c_data();
	return ret;
}


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

	for (i = 0; i < num_pins; i++, gpio_pin_ptr++) {
		int j;
		for (j=0; j < 10; j++) {
			ret = gpio_request(gpio_pin_ptr->num, NULL);
			if (!ret) {
				claimed_list[i] = gpio_pin_ptr->num;
				break;
			}
			msleep(100);
		}
		if (ret) {
			pr_err("request for gpio pin %s (%u) failed\n",
			       gpio_pin_ptr->name, gpio_pin_ptr->num);
			continue;
		}

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

		ret = gpio_sysfs_set_active_low(gpio_pin_ptr->num,
						gpio_pin_ptr->active_low);
		if (ret) {
			pr_err("unable to set active_low on gpio pin %s (%u)\n",
			       gpio_pin_ptr->name, gpio_pin_ptr->num);
			return ret;
		}

		ret = gpio_export(gpio_pin_ptr->num, false);
		if (ret) {
			pr_err("unable to export gpio pin %s (%u)\n",
			       gpio_pin_ptr->name, gpio_pin_ptr->num);
			return ret;
		}
	}
	return 0;
}

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

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

static int qct_ly4r_gpio_init(void)
{
	int i;
	int ret;

	for (i = 0; i < num_gpio_pins; i++)
		pca953x_gpio_names[qct_ly4r_gpio_pins[i].num -
			QCT_LY4R_GPIO_BASE] =
			qct_ly4r_gpio_pins[i].name;

	ret = init_gpio_pins(qct_ly4r_gpio_pins,
			     num_gpio_pins, claimed_gpio_pins);
	return ret;
}

static void __exit qct_ly4r_i2c_exit(void)
{
	free_i2c_data();
}

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

	ret = qct_ly4r_i2c_init();
	if (ret) {
		pr_err("Initializing I2C subsystem failed\n");
		return ret;
	}

	ret = qct_ly4r_gpio_init();
	if (ret) {
		pr_err("Initializing gpio subsystem failed\n");
		return ret;
	}

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

static void __exit
qct_ly4r_platform_exit(void)
{
	qct_ly4r_i2c_exit();
	free_gpio_pins(claimed_gpio_pins, num_gpio_pins);
	pr_info(DRIVER_NAME ": version " DRIVER_VERSION " unloaded\n");
}

module_init(qct_ly4r_platform_init);
module_exit(qct_ly4r_platform_exit);

MODULE_AUTHOR("Alan Liebthal (alanl@cumulusnetworks.com)");
MODULE_DESCRIPTION("Quanta LY4R Platform Support");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
