/*
 * Juniper Networks TMC GPIO driver
 *
 * Copyright (C) 2020 Juniper Networks
 * Author: Ashish Bhensdadia <bashish@juniper.net>
 *
 * This driver implement the GPIO set/get functionality
 *
 * 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 of the License.
 *
 * 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/device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include "jnx-tmc.h"

#define TMC_GPIO_MAX_BITS_PER_REG	  16
#define TMC_GPIO_SFP_MAX_BITS_PER_REG	  2
#define TMC_GPIO_PTPCFG_MAX_BITS_PER_REG  8

#define TMC_GPIO_FIND_GROUP(gpio)	\
			((gpio) / TMC_GPIO_MAX_BITS_PER_REG)
#define TMC_GPIO_FIND_GPIO(gpio)	\
			((gpio) % TMC_GPIO_MAX_BITS_PER_REG)

#define TMC_GPIO_SFP_FIND_GROUP(gpio)	\
			((gpio) / TMC_GPIO_SFP_MAX_BITS_PER_REG)
#define TMC_GPIO_SFP_FIND_GPIO(gpio)	\
			((gpio) % TMC_GPIO_SFP_MAX_BITS_PER_REG)

#define TMC_GPIO_PTPCFG_FIND_GPIO(gpio)	\
			((gpio) % TMC_GPIO_PTPCFG_MAX_BITS_PER_REG)

#define TMC_GPIO_MAX_NGPIO_PER_GROUP		320

#define TMC_PFE_QSFP_RESET_OFFSET       	0x4
#define TMC_PFE_QSFP_PRESENT_OFFSET		0x8
#define TMC_PFE_QSFP_PHY_RESET_OFFSET		0x10
#define TMC_PFE_QSFP_LPMOD_OFFSET		0x78
#define TMC_PFE_QSFP_LED_CTRL_OFFSET		0x20

#define TMC_PFE_LANES_GREEN_LED_VALUE         0x3
#define TMC_PFE_LANE0_GREEN_LED_BIT_POSITION  0
#define TMC_PFE_LANE1_GREEN_LED_BIT_POSITION  2
#define TMC_PFE_LANE2_GREEN_LED_BIT_POSITION  4
#define TMC_PFE_LANE3_GREEN_LED_BIT_POSITION  6

#define TMC_PFE_LANES_BEACON_LED_VALUE         0x2
#define TMC_PFE_LANE0_BEACON_LED_BIT_POSITION  0
#define TMC_PFE_LANE1_BEACON_LED_BIT_POSITION  2
#define TMC_PFE_LANE2_BEACON_LED_BIT_POSITION  4
#define TMC_PFE_LANE3_BEACON_LED_BIT_POSITION  6

#define TMC_PFE_LANES_FAULT_LED_VALUE         0x1
#define TMC_PFE_LANE0_FAULT_LED_BIT_POSITION  0
#define TMC_PFE_LANE1_FAULT_LED_BIT_POSITION  2
#define TMC_PFE_LANE2_FAULT_LED_BIT_POSITION  4
#define TMC_PFE_LANE3_FAULT_LED_BIT_POSITION  6

#define TMC_PFE_SFPSB0_TX_DISABLE_OFFSET   	0x0
#define TMC_PFE_SFPSB0_LED_CTRL_OFFSET     	0xC
#define TMC_PFE_SFPSB0_LED_ACTIVITY_OFFSET 	0x14
#define TMC_PFE_SFPSB0_PRESENT_OFFSET      	0x18
#define TMC_PFE_SFPSB0_LOSS_OFFSET         	0x1C
#define TMC_PFE_SFPSB0_TX_FAULT_OFFSET     	0x20

#define TMC_PFE_SFPSB1_TX_DISABLE_OFFSET   	0x0
#define TMC_PFE_SFPSB1_LED_CTRL_OFFSET     	0x8
#define TMC_PFE_SFPSB1_LED_ACTIVITY_OFFSET 	0x10
#define TMC_PFE_SFPSB1_PRESENT_OFFSET      	0x14
#define TMC_PFE_SFPSB1_LOSS_OFFSET         	0x18
#define TMC_PFE_SFPSB1_TX_FAULT_OFFSET     	0x1C

/*
 * Index 4 to 15 is used for QSFP starting with
 * QSFP_LED_LANE0_GREEN. To keep multibit set/get common
 * starting SFP_LED_LANE0_GREEN with 16 which will avoid 
 * conflict with QSFP enums.
 */
#define SFP_LED_OP_START_INDEX   	16

/*
 * Used for off-setting SFP led op index
 */
#define SFP_LED_OP_OFFSET   	0xB

/*
 * SFP slave blocks
 */
#define SFP_SLAVE0_BLOCK   	0x1
#define SFP_SLAVE1_BLOCK   	0x2

/*
 * each group represent the 16 gpios.
 * QSFP_RST - QSFP_LPMODE
 * 	each bit represent the one gpio
 *      exemple: bits[0:15] - bit0 - gpio0
 * QSFP_LED_LANE0_GREEN - QSFP_LED_LANE3_FAULT
 *	here, number represent the one gpio
 * 	exemple: bits[0:1]
 *	00 - gpio off, 01 - gpio on [ gpio0]
 *      00 - gpio off, 10 - gpio on [ gpio1]
 *      00 - gpio off, 11 - gpio on [ gpio2]
 *
 */
enum {
	QSFP_RST,
	QSFP_PRESENT,
	QSFP_PHY_RST,
	QSFP_LPMOD,
	QSFP_LED_LANE0_GREEN,
	QSFP_LED_LANE1_GREEN,
	QSFP_LED_LANE2_GREEN,
	QSFP_LED_LANE3_GREEN,
	QSFP_LED_LANE0_BEACON,
	QSFP_LED_LANE1_BEACON,
	QSFP_LED_LANE2_BEACON,
	QSFP_LED_LANE3_BEACON,
	QSFP_LED_LANE0_FAULT,
	QSFP_LED_LANE1_FAULT,
	QSFP_LED_LANE2_FAULT,
	QSFP_LED_LANE3_FAULT,
	TMC_PFE_GPIO_GROUP_MAX
};

enum sfp_op {
	SFP_TX_DISABLE,
	SFP_LED_ACTIVITY,
	SFP_PRESENT,
	SFP_SFP_LOS,
	SFP_TX_FAULT,
	SFP_LED_LANE0_GREEN = SFP_LED_OP_START_INDEX,
	SFP_LED_LANE1_GREEN,
	SFP_LED_LANE2_GREEN,
	SFP_LED_LANE3_GREEN,
	SFP_LED_LANE0_BEACON,
	SFP_LED_LANE1_BEACON,
	SFP_LED_LANE2_BEACON,
	SFP_LED_LANE3_BEACON,
	SFP_LED_LANE0_FAULT,
	SFP_LED_LANE1_FAULT,
	SFP_LED_LANE2_FAULT,
	SFP_LED_LANE3_FAULT,
	TMC_PFE_SFP_GPIO_GROUP_MAX
};

static const u32 group_offset[TMC_PFE_GPIO_GROUP_MAX] = {
			TMC_PFE_QSFP_RESET_OFFSET,
			TMC_PFE_QSFP_PRESENT_OFFSET,
			TMC_PFE_QSFP_PHY_RESET_OFFSET,
			TMC_PFE_QSFP_LPMOD_OFFSET,
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE0 GREEN */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE1 GREEN */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE2 GREEN */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE3 GREEN */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE0 BEACON */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE1 BEACON */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE2 BEACON */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE3 BEACON */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE0 FAULT */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE1 FAULT */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE2 FAULT */
			TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE3 FAULT */
};

static const u32 sfp_slaveb0_group_offset[TMC_PFE_SFP_GPIO_GROUP_MAX] = {
			TMC_PFE_SFPSB0_TX_DISABLE_OFFSET,
			TMC_PFE_SFPSB0_LED_ACTIVITY_OFFSET,
			TMC_PFE_SFPSB0_PRESENT_OFFSET,
			TMC_PFE_SFPSB0_LOSS_OFFSET,
			TMC_PFE_SFPSB0_TX_FAULT_OFFSET,
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE0 GREEN */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE1 GREEN */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE2 GREEN */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE3 GREEN */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE0 BEACON */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE1 BEACON */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE2 BEACON */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE3 BEACON */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE0 FAULT */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE1 FAULT */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE2 FAULT */
			TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE3 FAULT */
};

static const u32 sfp_slaveb1_group_offset[TMC_PFE_SFP_GPIO_GROUP_MAX] = {
			TMC_PFE_SFPSB1_TX_DISABLE_OFFSET,
			TMC_PFE_SFPSB1_LED_ACTIVITY_OFFSET,
			TMC_PFE_SFPSB1_PRESENT_OFFSET,
			TMC_PFE_SFPSB1_LOSS_OFFSET,
			TMC_PFE_SFPSB1_TX_FAULT_OFFSET,
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE0 GREEN */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE1 GREEN */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE2 GREEN */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE3 GREEN */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE0 BEACON */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE1 BEACON */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE2 BEACON */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE3 BEACON */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE0 FAULT */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE1 FAULT */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE2 FAULT */
			TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE3 FAULT */
};

struct tmc_gpio_info {
	int (*get)(struct gpio_chip *, unsigned int);
	void (*set)(struct gpio_chip *, unsigned int, int);
	int (*dirin)(struct gpio_chip *, unsigned int);
	int (*dirout)(struct gpio_chip *, unsigned int, int);
};

struct tmc_gpio_chip {
	const struct tmc_gpio_info *info;
	void __iomem *base;
	struct device *dev;
	struct gpio_chip gpio;
	int ngpio;
	spinlock_t gpio_lock; /* gpio lock */
	int sfp_slave_block;
};

/* slave gpio max */
static int gpio_max = 320;
module_param(gpio_max, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(gpio_max, "Maximum number of gpio for SLAVE TMC GPIO");

/*
 * generic bit operation functions
 */
static u32 tmc_gpio_reset_bits(u32 state, u32 val, u32 shift)
{
	state &= ~(val << shift);
	return state;
};

static u32 tmc_gpio_set_bits(u32 state, u32 val, u32 shift)
{
	state |= (val << shift);
	return state;
};

static u32 tmc_gpio_find_bits_val(u32 state, u32 shift, u32 mask)
{
	return ((state >> shift)) & mask;
};

#define to_tmc_chip(chip) \
	container_of((chip), struct tmc_gpio_chip, gpio)

/*
 * tmc_gpio_multiple_bitsop - Generic TMC GPIO multiple bits operation
 */
static void tmc_gpio_multiple_bitsop(struct tmc_gpio_chip *chip,
				unsigned int gpiono, u32 group, u32 offset, bool set)
{
	u32 gpio_state, led_val, bit_shift;
	unsigned long flags;
	void __iomem *iobase;

	iobase = chip->base + offset;

	dev_dbg(chip->dev, "TMC GPIO multiple bitop group=%u, "
		"gpiono=%u, offet:=%u, set=%u\n", group, gpiono, offset, set);

	spin_lock_irqsave(&chip->gpio_lock, flags);

	switch (group) {
	case QSFP_LED_LANE0_GREEN:
	case SFP_LED_LANE0_GREEN:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
		bit_shift = TMC_PFE_LANE0_GREEN_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE1_GREEN:
	case SFP_LED_LANE1_GREEN:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
		bit_shift = TMC_PFE_LANE1_GREEN_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE2_GREEN:
	case SFP_LED_LANE2_GREEN:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
		bit_shift = TMC_PFE_LANE2_GREEN_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE3_GREEN:
	case SFP_LED_LANE3_GREEN:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
		bit_shift = TMC_PFE_LANE3_GREEN_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE0_BEACON:
	case SFP_LED_LANE0_BEACON:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
		bit_shift = TMC_PFE_LANE0_BEACON_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE1_BEACON:
	case SFP_LED_LANE1_BEACON:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
		bit_shift = TMC_PFE_LANE1_BEACON_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE2_BEACON:
	case SFP_LED_LANE2_BEACON:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
		bit_shift = TMC_PFE_LANE2_BEACON_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE3_BEACON:
	case SFP_LED_LANE3_BEACON:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
		bit_shift = TMC_PFE_LANE3_BEACON_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE0_FAULT:
	case SFP_LED_LANE0_FAULT:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
		bit_shift = TMC_PFE_LANE0_FAULT_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE1_FAULT:
	case SFP_LED_LANE1_FAULT:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
		bit_shift = TMC_PFE_LANE1_FAULT_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE2_FAULT:
	case SFP_LED_LANE2_FAULT:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
		bit_shift = TMC_PFE_LANE2_FAULT_LED_BIT_POSITION;
		break;
	case QSFP_LED_LANE3_FAULT:
	case SFP_LED_LANE3_FAULT:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
		bit_shift = TMC_PFE_LANE3_FAULT_LED_BIT_POSITION;
		break;

	default:
		spin_unlock_irqrestore(&chip->gpio_lock, flags);
		return;
	}

	if (set) {
		gpio_state = tmc_gpio_reset_bits(gpio_state, 0x3, bit_shift);
		gpio_state = tmc_gpio_set_bits(gpio_state, led_val, bit_shift);
	} else {
		gpio_state = tmc_gpio_reset_bits(gpio_state, 0x3, bit_shift);
	}

	iowrite32(gpio_state, (iobase+(0x004*gpiono)));

	spin_unlock_irqrestore(&chip->gpio_lock, flags);

	return;
};

/*
 * tmc_gpio_one_bitop - Generic TMC GPIO single bit operation
 */
static void tmc_gpio_one_bitop(struct tmc_gpio_chip *chip,
				unsigned int bit, u32 offset, bool set)
{
	u32 gpio_state;
	unsigned long flags;
	void __iomem *iobase;

	iobase = chip->base + offset;

	dev_dbg(chip->dev, "TMC GPIO one bitop bit=%u, offset=%x, "
		"set=%u\n", bit, offset, set);

	spin_lock_irqsave(&chip->gpio_lock, flags);

	gpio_state = ioread32(iobase);
	if (set)
		gpio_state |= BIT(bit);
	else
		gpio_state &= ~BIT(bit);

	iowrite32(gpio_state, iobase);

	spin_unlock_irqrestore(&chip->gpio_lock, flags);

	return;
}

/*
 * tmc_gpio_get_multiple_bitsop - Generic TMC get GPIO multiple bits operation
 */
static int tmc_gpio_get_multiple_bitsop(struct tmc_gpio_chip *chip,
				unsigned int gpiono, u32 group, u32 offset)
{
	u32 gpio_state;
	void __iomem *iobase;

	iobase = chip->base + offset;

	dev_dbg(chip->dev, "TMC GPIO get multiple bitsop group=%u, "
		"gpiono=%u, offset=%u\n", group, gpiono, offset);

	switch (group) {
	case QSFP_LED_LANE0_GREEN:
	case SFP_LED_LANE0_GREEN:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_GREEN_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE0_GREEN_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE1_GREEN:
	case SFP_LED_LANE1_GREEN:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_GREEN_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE1_GREEN_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE2_GREEN:
	case SFP_LED_LANE2_GREEN:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_GREEN_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE2_GREEN_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE3_GREEN:
	case SFP_LED_LANE3_GREEN:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_GREEN_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE3_GREEN_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE0_BEACON:
	case SFP_LED_LANE0_BEACON:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_BEACON_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE0_BEACON_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE1_BEACON:
	case SFP_LED_LANE1_BEACON:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_BEACON_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE1_BEACON_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE2_BEACON:
	case SFP_LED_LANE2_BEACON:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_BEACON_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE2_BEACON_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE3_BEACON:
	case SFP_LED_LANE3_BEACON:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_BEACON_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE3_BEACON_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE0_FAULT:
	case SFP_LED_LANE0_FAULT:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_FAULT_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE0_FAULT_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE1_FAULT:
	case SFP_LED_LANE1_FAULT:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_FAULT_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE1_FAULT_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE2_FAULT:
	case SFP_LED_LANE2_FAULT:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_FAULT_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE2_FAULT_LED_BIT_POSITION, 0x3));
	case QSFP_LED_LANE3_FAULT:
	case SFP_LED_LANE3_FAULT:
		gpio_state = ioread32(iobase+(0x004*gpiono));
		return (TMC_PFE_LANES_FAULT_LED_VALUE ==
			tmc_gpio_find_bits_val(gpio_state,
			TMC_PFE_LANE3_FAULT_LED_BIT_POSITION, 0x3));
	default:
		return 0;
	}
};

/*
 * tmc_gpio_get - Read the specified signal of the GPIO device.
 */
static int tmc_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{
	struct tmc_gpio_chip *chip = to_tmc_chip(gc);
	unsigned int group = TMC_GPIO_FIND_GROUP(gpio);
	unsigned int bit   = TMC_GPIO_FIND_GPIO(gpio);

	if (group >= TMC_PFE_GPIO_GROUP_MAX)
		return 0;

	switch (group) {
	case QSFP_RST:
	case QSFP_PRESENT:
	case QSFP_PHY_RST:
	case QSFP_LPMOD:
		dev_dbg(chip->dev, "TMC GPIO get one bitop group=%u, gpio=%u, "
			"bit=%u\n", group, gpio, bit);
		return !!(ioread32(chip->base + group_offset[group])
				& BIT(bit));
	default:
		return tmc_gpio_get_multiple_bitsop(chip, bit, group, group_offset[group]);
	}
}

/*
 * tmc_gpio_set - Write the specified signal of the GPIO device.
 */
static void tmc_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
	struct tmc_gpio_chip *chip = to_tmc_chip(gc);
	unsigned int group = TMC_GPIO_FIND_GROUP(gpio);
	unsigned int bit   = TMC_GPIO_FIND_GPIO(gpio);

	if (group >= TMC_PFE_GPIO_GROUP_MAX)
		return;

	switch (group) {
	case QSFP_RST:
	case QSFP_PRESENT:
	case QSFP_PHY_RST:
	case QSFP_LPMOD:
		dev_dbg(chip->dev, "TMC GPIO one bitop group=%d\n", group);
		tmc_gpio_one_bitop(chip, bit, group_offset[group], val);
		break;
	default:
		tmc_gpio_multiple_bitsop(chip, bit, group, group_offset[group], val);
		break;
	}
}

static struct tmc_gpio_info tmc_gpios[] = {
	{
	    .get = tmc_gpio_get,
	    .set = tmc_gpio_set,
	},
};

static void tmc_gpio_setup(struct tmc_gpio_chip *sgc, int id)
{
	struct gpio_chip *chip = &sgc->gpio;
	const struct tmc_gpio_info *info = sgc->info;

	chip->get		= info->get;
	chip->set		= info->set;
	chip->direction_input	= info->dirin;
	chip->direction_output	= info->dirout;
	chip->dbg_show		= NULL;
	chip->can_sleep		= 0;

        if (id == 0) {
            chip->base = 0;
        } else if (id == 1) {
            chip->base = (gpio_max * id);
        } else { 
	    chip->base	= -1;
        }

	chip->ngpio	= sgc->ngpio;
	chip->label	= dev_name(sgc->dev);
	chip->parent	= sgc->dev;
	chip->owner	= THIS_MODULE;
}

static int tmc_gpio_of_init(struct device *dev,
				struct tmc_gpio_chip *chip)
{
        chip->info = &tmc_gpios[0];
        chip->ngpio = gpio_max;

        return 0;
}

static int tmc_gpio_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct tmc_gpio_chip *chip;
	struct resource *res;
	int ret;

        const struct mfd_cell *cell = mfd_get_cell(pdev);

	dev_dbg(dev, "TMC GPIO probe\n");

	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res)
		return -ENODEV;

	dev_info(dev, "TMC GPIO resource 0x%llx, %llu\n",
		 res->start, resource_size(res));

	chip->base = devm_ioremap(dev, res->start, resource_size(res));
	if (!chip->base)
		return -ENOMEM;

	ret = tmc_gpio_of_init(dev, chip);
	if (ret)
		return ret;

	chip->dev = dev;
	spin_lock_init(&chip->gpio_lock);

	tmc_gpio_setup(chip, cell->id);

	ret = gpiochip_add(&chip->gpio);
	if (ret) {
		dev_err(dev,
			"Failed to register TMC gpiochip : %d\n", ret);
		return ret;
	}

	platform_set_drvdata(pdev, chip);
	dev_info(dev, "TMC GPIO registered at 0x%lx, gpiobase: %d\n",
		 (long unsigned)chip->base, chip->gpio.base);

	return 0;
}

static int tmc_gpio_remove(struct platform_device *pdev)
{
	struct tmc_gpio_chip *chip = platform_get_drvdata(pdev);

	gpiochip_remove(&chip->gpio);

	return 0;
}

static struct platform_driver tmc_gpio_driver = {
	.driver = {
		.name = "gpioslave-tmc",
		.owner  = THIS_MODULE,
	},
	.probe = tmc_gpio_probe,
	.remove = tmc_gpio_remove,
};

module_platform_driver(tmc_gpio_driver);

MODULE_DESCRIPTION("Juniper Networks TMC FPGA GPIO driver");
MODULE_AUTHOR("Ashish Bhensdadia <bashish@juniper.net>");
MODULE_LICENSE("GPL");