// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2020 - 2021 NVIDIA CORPORATION & AFFILIATES
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/resource.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/types.h>
/*
* There are 3 YU GPIO blocks :
* gpio [ 0 ] : HOST_GPIO0 - > HOST_GPIO31
* gpio [ 1 ] : HOST_GPIO32 - > HOST_GPIO63
* gpio [ 2 ] : HOST_GPIO64 - > HOST_GPIO69
*/
#define MLXBF2_GPIO_MAX_PINS_PER_BLOCK 32
/*
* arm_gpio_lock register :
* bit [ 31 ] lock status : active if set
* bit [ 15 : 0 ] set lock
* The lock is enabled only if 0 xd42f is written to this field
*/
#define YU_ARM_GPIO_LOCK_ADDR 0 x2801088
#define YU_ARM_GPIO_LOCK_SIZE 0 x8
#define YU_LOCK_ACTIVE_BIT(val) (val >> 31 )
#define YU_ARM_GPIO_LOCK_ACQUIRE 0 xd42f
#define YU_ARM_GPIO_LOCK_RELEASE 0 x0
/*
* gpio [ x ] block registers and their offset
*/
#define YU_GPIO_DATAIN 0 x04
#define YU_GPIO_MODE1 0 x08
#define YU_GPIO_MODE0 0 x0c
#define YU_GPIO_DATASET 0 x14
#define YU_GPIO_DATACLEAR 0 x18
#define YU_GPIO_CAUSE_RISE_EN 0 x44
#define YU_GPIO_CAUSE_FALL_EN 0 x48
#define YU_GPIO_MODE1_CLEAR 0 x50
#define YU_GPIO_MODE0_SET 0 x54
#define YU_GPIO_MODE0_CLEAR 0 x58
#define YU_GPIO_CAUSE_OR_CAUSE_EVTEN0 0 x80
#define YU_GPIO_CAUSE_OR_EVTEN0 0 x94
#define YU_GPIO_CAUSE_OR_CLRCAUSE 0 x98
struct mlxbf2_gpio_context_save_regs {
u32 gpio_mode0;
u32 gpio_mode1;
};
/* BlueField-2 gpio block context structure. */
struct mlxbf2_gpio_context {
struct gpio_chip gc;
/* YU GPIO blocks address */
void __iomem *gpio_io;
struct device *dev;
struct mlxbf2_gpio_context_save_regs *csave_regs;
};
/* BlueField-2 gpio shared structure. */
struct mlxbf2_gpio_param {
void __iomem *io;
struct resource *res;
struct mutex *lock;
};
static struct resource yu_arm_gpio_lock_res =
DEFINE_RES_MEM_NAMED(YU_ARM_GPIO_LOCK_ADDR, YU_ARM_GPIO_LOCK_SIZE, "YU_ARM_GPIO_LOCK" );
static DEFINE_MUTEX(yu_arm_gpio_lock_mutex);
static struct mlxbf2_gpio_param yu_arm_gpio_lock_param = {
.res = &yu_arm_gpio_lock_res,
.lock = &yu_arm_gpio_lock_mutex,
};
/* Request memory region and map yu_arm_gpio_lock resource */
static int mlxbf2_gpio_get_lock_res(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
resource_size_t size;
int ret = 0 ;
mutex_lock(yu_arm_gpio_lock_param.lock);
/* Check if the memory map already exists */
if (yu_arm_gpio_lock_param.io)
goto exit ;
res = yu_arm_gpio_lock_param.res;
size = resource_size(res);
if (!devm_request_mem_region(dev, res->start, size, res->name)) {
ret = -EFAULT;
goto exit ;
}
yu_arm_gpio_lock_param.io = devm_ioremap(dev, res->start, size);
if (!yu_arm_gpio_lock_param.io)
ret = -ENOMEM;
exit :
mutex_unlock(yu_arm_gpio_lock_param.lock);
return ret;
}
/*
* Acquire the YU arm_gpio_lock to be able to change the direction
* mode . If the lock_active bit is already set , return an error .
*/
static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs)
{
u32 arm_gpio_lock_val;
mutex_lock(yu_arm_gpio_lock_param.lock);
raw_spin_lock(&gs->gc.bgpio_lock);
arm_gpio_lock_val = readl(yu_arm_gpio_lock_param.io);
/*
* When lock active bit [ 31 ] is set , ModeX is write enabled
*/
if (YU_LOCK_ACTIVE_BIT(arm_gpio_lock_val)) {
raw_spin_unlock(&gs->gc.bgpio_lock);
mutex_unlock(yu_arm_gpio_lock_param.lock);
return -EINVAL;
}
writel(YU_ARM_GPIO_LOCK_ACQUIRE, yu_arm_gpio_lock_param.io);
return 0 ;
}
/*
* Release the YU arm_gpio_lock after changing the direction mode .
*/
static void mlxbf2_gpio_lock_release(struct mlxbf2_gpio_context *gs)
__releases(&gs->gc.bgpio_lock)
__releases(yu_arm_gpio_lock_param.lock)
{
writel(YU_ARM_GPIO_LOCK_RELEASE, yu_arm_gpio_lock_param.io);
raw_spin_unlock(&gs->gc.bgpio_lock);
mutex_unlock(yu_arm_gpio_lock_param.lock);
}
/*
* mode0 and mode1 are both locked by the gpio_lock field .
*
* Together , mode0 and mode1 define the gpio Mode dependeing also
* on Reg_DataOut .
*
* { mode1 , mode0 } : { Reg_DataOut = 0 , Reg_DataOut = 1 } - > { DataOut = 0 , DataOut = 1 }
*
* { 0 , 0 } : Reg_DataOut { 0 , 1 } - > { Z , Z } Input PAD
* { 0 , 1 } : Reg_DataOut { 0 , 1 } - > { 0 , 1 } Full drive Output PAD
* { 1 , 0 } : Reg_DataOut { 0 , 1 } - > { 0 , Z } 0 - set PAD to low , 1 - float
* { 1 , 1 } : Reg_DataOut { 0 , 1 } - > { Z , 1 } 0 - float , 1 - set PAD to high
*/
/*
* Set input direction :
* { mode1 , mode0 } = { 0 , 0 }
*/
static int mlxbf2_gpio_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
struct mlxbf2_gpio_context *gs = gpiochip_get_data(chip);
int ret;
/*
* Although the arm_gpio_lock was set in the probe function , check again
* if it is still enabled to be able to write to the ModeX registers .
*/
ret = mlxbf2_gpio_lock_acquire(gs);
if (ret < 0 )
return ret;
writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE0_CLEAR);
writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE1_CLEAR);
mlxbf2_gpio_lock_release(gs);
return ret;
}
/*
* Set output direction :
* { mode1 , mode0 } = { 0 , 1 }
*/
static int mlxbf2_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset,
int value)
{
struct mlxbf2_gpio_context *gs = gpiochip_get_data(chip);
int ret = 0 ;
/*
* Although the arm_gpio_lock was set in the probe function ,
* check again it is still enabled to be able to write to the
* ModeX registers .
*/
ret = mlxbf2_gpio_lock_acquire(gs);
if (ret < 0 )
return ret;
writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE1_CLEAR);
writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE0_SET);
mlxbf2_gpio_lock_release(gs);
return ret;
}
static void mlxbf2_gpio_irq_enable(struct irq_data *irqd)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc);
int offset = irqd_to_hwirq(irqd);
unsigned long flags;
u32 val;
gpiochip_enable_irq(gc, irqd_to_hwirq(irqd));
raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE);
val |= BIT(offset);
writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE);
val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
val |= BIT(offset);
writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
}
static void mlxbf2_gpio_irq_disable(struct irq_data *irqd)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc);
int offset = irqd_to_hwirq(irqd);
unsigned long flags;
u32 val;
raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
val &= ~BIT(offset);
writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
gpiochip_disable_irq(gc, irqd_to_hwirq(irqd));
}
static irqreturn_t mlxbf2_gpio_irq_handler(int irq, void *ptr)
{
struct mlxbf2_gpio_context *gs = ptr;
struct gpio_chip *gc = &gs->gc;
unsigned long pending;
u32 level;
pending = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CAUSE_EVTEN0);
writel(pending, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE);
for_each_set_bit(level, &pending, gc->ngpio)
generic_handle_domain_irq_safe(gc->irq.domain, level);
return IRQ_RETVAL(pending);
}
static int
mlxbf2_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc);
int offset = irqd_to_hwirq(irqd);
unsigned long flags;
bool fall = false ;
bool rise = false ;
u32 val;
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_BOTH:
fall = true ;
rise = true ;
break ;
case IRQ_TYPE_EDGE_RISING:
rise = true ;
break ;
case IRQ_TYPE_EDGE_FALLING:
fall = true ;
break ;
default :
return -EINVAL;
}
raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
if (fall) {
val = readl(gs->gpio_io + YU_GPIO_CAUSE_FALL_EN);
val |= BIT(offset);
writel(val, gs->gpio_io + YU_GPIO_CAUSE_FALL_EN);
}
if (rise) {
val = readl(gs->gpio_io + YU_GPIO_CAUSE_RISE_EN);
val |= BIT(offset);
writel(val, gs->gpio_io + YU_GPIO_CAUSE_RISE_EN);
}
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
return 0 ;
}
static void mlxbf2_gpio_irq_print_chip(struct irq_data *irqd,
struct seq_file *p)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc);
seq_puts(p, dev_name(gs->dev));
}
static const struct irq_chip mlxbf2_gpio_irq_chip = {
.irq_set_type = mlxbf2_gpio_irq_set_type,
.irq_enable = mlxbf2_gpio_irq_enable,
.irq_disable = mlxbf2_gpio_irq_disable,
.irq_print_chip = mlxbf2_gpio_irq_print_chip,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
/* BlueField-2 GPIO driver initialization routine. */
static int
mlxbf2_gpio_probe(struct platform_device *pdev)
{
struct mlxbf2_gpio_context *gs;
struct device *dev = &pdev->dev;
struct gpio_irq_chip *girq;
struct gpio_chip *gc;
unsigned int npins;
const char *name;
int ret, irq;
name = dev_name(dev);
gs = devm_kzalloc(dev, sizeof (*gs), GFP_KERNEL);
if (!gs)
return -ENOMEM;
gs->dev = dev;
/* YU GPIO block address */
gs->gpio_io = devm_platform_ioremap_resource(pdev, 0 );
if (IS_ERR(gs->gpio_io))
return PTR_ERR(gs->gpio_io);
ret = mlxbf2_gpio_get_lock_res(pdev);
if (ret) {
dev_err(dev, "Failed to get yu_arm_gpio_lock resource\n" );
return ret;
}
if (device_property_read_u32(dev, "npins" , &npins))
npins = MLXBF2_GPIO_MAX_PINS_PER_BLOCK;
gc = &gs->gc;
ret = bgpio_init(gc, dev, 4 ,
gs->gpio_io + YU_GPIO_DATAIN,
gs->gpio_io + YU_GPIO_DATASET,
gs->gpio_io + YU_GPIO_DATACLEAR,
NULL,
NULL,
0 );
if (ret) {
dev_err(dev, "bgpio_init failed\n" );
return ret;
}
gc->direction_input = mlxbf2_gpio_direction_input;
gc->direction_output = mlxbf2_gpio_direction_output;
gc->ngpio = npins;
gc->owner = THIS_MODULE;
irq = platform_get_irq_optional(pdev, 0 );
if (irq >= 0 ) {
girq = &gs->gc.irq;
gpio_irq_chip_set_chip(girq, &mlxbf2_gpio_irq_chip);
girq->handler = handle_simple_irq;
girq->default_type = IRQ_TYPE_NONE;
/* This will let us handle the parent IRQ in the driver */
girq->num_parents = 0 ;
girq->parents = NULL;
girq->parent_handler = NULL;
/*
* Directly request the irq here instead of passing
* a flow - handler because the irq is shared .
*/
ret = devm_request_irq(dev, irq, mlxbf2_gpio_irq_handler,
IRQF_SHARED, name, gs);
if (ret) {
dev_err(dev, "failed to request IRQ" );
return ret;
}
}
platform_set_drvdata(pdev, gs);
ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
if (ret) {
dev_err(dev, "Failed adding memory mapped gpiochip\n" );
return ret;
}
return 0 ;
}
static int __maybe_unused mlxbf2_gpio_suspend(struct device *dev)
{
struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev);
gs->csave_regs->gpio_mode0 = readl(gs->gpio_io +
YU_GPIO_MODE0);
gs->csave_regs->gpio_mode1 = readl(gs->gpio_io +
YU_GPIO_MODE1);
return 0 ;
}
static int __maybe_unused mlxbf2_gpio_resume(struct device *dev)
{
struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev);
writel(gs->csave_regs->gpio_mode0, gs->gpio_io +
YU_GPIO_MODE0);
writel(gs->csave_regs->gpio_mode1, gs->gpio_io +
YU_GPIO_MODE1);
return 0 ;
}
static SIMPLE_DEV_PM_OPS(mlxbf2_pm_ops, mlxbf2_gpio_suspend, mlxbf2_gpio_resume);
static const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = {
{ "MLNXBF22" , 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, mlxbf2_gpio_acpi_match);
static struct platform_driver mlxbf2_gpio_driver = {
.driver = {
.name = "mlxbf2_gpio" ,
.acpi_match_table = mlxbf2_gpio_acpi_match,
.pm = &mlxbf2_pm_ops,
},
.probe = mlxbf2_gpio_probe,
};
module_platform_driver(mlxbf2_gpio_driver);
MODULE_DESCRIPTION("Mellanox BlueField-2 GPIO Driver" );
MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>" );
MODULE_LICENSE("GPL v2" );
Messung V0.5 in Prozent C=95 H=98 G=96
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland