sonic-buildimage/platform/broadcom/sonic-platform-modules-juniper/qfx5200/modules/gpio-tmc.c

662 lines
19 KiB
C
Raw Normal View History

/*
* 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_nocache(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");