162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/bitfield.h> 862306a36Sopenharmony_ci#include <linux/bitops.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/ioport.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/pm.h> 1962306a36Sopenharmony_ci#include <linux/resource.h> 2062306a36Sopenharmony_ci#include <linux/seq_file.h> 2162306a36Sopenharmony_ci#include <linux/spinlock.h> 2262306a36Sopenharmony_ci#include <linux/types.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * There are 3 YU GPIO blocks: 2662306a36Sopenharmony_ci * gpio[0]: HOST_GPIO0->HOST_GPIO31 2762306a36Sopenharmony_ci * gpio[1]: HOST_GPIO32->HOST_GPIO63 2862306a36Sopenharmony_ci * gpio[2]: HOST_GPIO64->HOST_GPIO69 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci#define MLXBF2_GPIO_MAX_PINS_PER_BLOCK 32 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * arm_gpio_lock register: 3462306a36Sopenharmony_ci * bit[31] lock status: active if set 3562306a36Sopenharmony_ci * bit[15:0] set lock 3662306a36Sopenharmony_ci * The lock is enabled only if 0xd42f is written to this field 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci#define YU_ARM_GPIO_LOCK_ADDR 0x2801088 3962306a36Sopenharmony_ci#define YU_ARM_GPIO_LOCK_SIZE 0x8 4062306a36Sopenharmony_ci#define YU_LOCK_ACTIVE_BIT(val) (val >> 31) 4162306a36Sopenharmony_ci#define YU_ARM_GPIO_LOCK_ACQUIRE 0xd42f 4262306a36Sopenharmony_ci#define YU_ARM_GPIO_LOCK_RELEASE 0x0 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * gpio[x] block registers and their offset 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci#define YU_GPIO_DATAIN 0x04 4862306a36Sopenharmony_ci#define YU_GPIO_MODE1 0x08 4962306a36Sopenharmony_ci#define YU_GPIO_MODE0 0x0c 5062306a36Sopenharmony_ci#define YU_GPIO_DATASET 0x14 5162306a36Sopenharmony_ci#define YU_GPIO_DATACLEAR 0x18 5262306a36Sopenharmony_ci#define YU_GPIO_CAUSE_RISE_EN 0x44 5362306a36Sopenharmony_ci#define YU_GPIO_CAUSE_FALL_EN 0x48 5462306a36Sopenharmony_ci#define YU_GPIO_MODE1_CLEAR 0x50 5562306a36Sopenharmony_ci#define YU_GPIO_MODE0_SET 0x54 5662306a36Sopenharmony_ci#define YU_GPIO_MODE0_CLEAR 0x58 5762306a36Sopenharmony_ci#define YU_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x80 5862306a36Sopenharmony_ci#define YU_GPIO_CAUSE_OR_EVTEN0 0x94 5962306a36Sopenharmony_ci#define YU_GPIO_CAUSE_OR_CLRCAUSE 0x98 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct mlxbf2_gpio_context_save_regs { 6262306a36Sopenharmony_ci u32 gpio_mode0; 6362306a36Sopenharmony_ci u32 gpio_mode1; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* BlueField-2 gpio block context structure. */ 6762306a36Sopenharmony_cistruct mlxbf2_gpio_context { 6862306a36Sopenharmony_ci struct gpio_chip gc; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* YU GPIO blocks address */ 7162306a36Sopenharmony_ci void __iomem *gpio_io; 7262306a36Sopenharmony_ci struct device *dev; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci struct mlxbf2_gpio_context_save_regs *csave_regs; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* BlueField-2 gpio shared structure. */ 7862306a36Sopenharmony_cistruct mlxbf2_gpio_param { 7962306a36Sopenharmony_ci void __iomem *io; 8062306a36Sopenharmony_ci struct resource *res; 8162306a36Sopenharmony_ci struct mutex *lock; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic struct resource yu_arm_gpio_lock_res = 8562306a36Sopenharmony_ci DEFINE_RES_MEM_NAMED(YU_ARM_GPIO_LOCK_ADDR, YU_ARM_GPIO_LOCK_SIZE, "YU_ARM_GPIO_LOCK"); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic DEFINE_MUTEX(yu_arm_gpio_lock_mutex); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic struct mlxbf2_gpio_param yu_arm_gpio_lock_param = { 9062306a36Sopenharmony_ci .res = &yu_arm_gpio_lock_res, 9162306a36Sopenharmony_ci .lock = &yu_arm_gpio_lock_mutex, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* Request memory region and map yu_arm_gpio_lock resource */ 9562306a36Sopenharmony_cistatic int mlxbf2_gpio_get_lock_res(struct platform_device *pdev) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 9862306a36Sopenharmony_ci struct resource *res; 9962306a36Sopenharmony_ci resource_size_t size; 10062306a36Sopenharmony_ci int ret = 0; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci mutex_lock(yu_arm_gpio_lock_param.lock); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Check if the memory map already exists */ 10562306a36Sopenharmony_ci if (yu_arm_gpio_lock_param.io) 10662306a36Sopenharmony_ci goto exit; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci res = yu_arm_gpio_lock_param.res; 10962306a36Sopenharmony_ci size = resource_size(res); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (!devm_request_mem_region(dev, res->start, size, res->name)) { 11262306a36Sopenharmony_ci ret = -EFAULT; 11362306a36Sopenharmony_ci goto exit; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci yu_arm_gpio_lock_param.io = devm_ioremap(dev, res->start, size); 11762306a36Sopenharmony_ci if (!yu_arm_gpio_lock_param.io) 11862306a36Sopenharmony_ci ret = -ENOMEM; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciexit: 12162306a36Sopenharmony_ci mutex_unlock(yu_arm_gpio_lock_param.lock); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return ret; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * Acquire the YU arm_gpio_lock to be able to change the direction 12862306a36Sopenharmony_ci * mode. If the lock_active bit is already set, return an error. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci u32 arm_gpio_lock_val; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci mutex_lock(yu_arm_gpio_lock_param.lock); 13562306a36Sopenharmony_ci raw_spin_lock(&gs->gc.bgpio_lock); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci arm_gpio_lock_val = readl(yu_arm_gpio_lock_param.io); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* 14062306a36Sopenharmony_ci * When lock active bit[31] is set, ModeX is write enabled 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci if (YU_LOCK_ACTIVE_BIT(arm_gpio_lock_val)) { 14362306a36Sopenharmony_ci raw_spin_unlock(&gs->gc.bgpio_lock); 14462306a36Sopenharmony_ci mutex_unlock(yu_arm_gpio_lock_param.lock); 14562306a36Sopenharmony_ci return -EINVAL; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci writel(YU_ARM_GPIO_LOCK_ACQUIRE, yu_arm_gpio_lock_param.io); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * Release the YU arm_gpio_lock after changing the direction mode. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_cistatic void mlxbf2_gpio_lock_release(struct mlxbf2_gpio_context *gs) 15762306a36Sopenharmony_ci __releases(&gs->gc.bgpio_lock) 15862306a36Sopenharmony_ci __releases(yu_arm_gpio_lock_param.lock) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci writel(YU_ARM_GPIO_LOCK_RELEASE, yu_arm_gpio_lock_param.io); 16162306a36Sopenharmony_ci raw_spin_unlock(&gs->gc.bgpio_lock); 16262306a36Sopenharmony_ci mutex_unlock(yu_arm_gpio_lock_param.lock); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * mode0 and mode1 are both locked by the gpio_lock field. 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * Together, mode0 and mode1 define the gpio Mode dependeing also 16962306a36Sopenharmony_ci * on Reg_DataOut. 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * {mode1,mode0}:{Reg_DataOut=0,Reg_DataOut=1}->{DataOut=0,DataOut=1} 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * {0,0}:Reg_DataOut{0,1}->{Z,Z} Input PAD 17462306a36Sopenharmony_ci * {0,1}:Reg_DataOut{0,1}->{0,1} Full drive Output PAD 17562306a36Sopenharmony_ci * {1,0}:Reg_DataOut{0,1}->{0,Z} 0-set PAD to low, 1-float 17662306a36Sopenharmony_ci * {1,1}:Reg_DataOut{0,1}->{Z,1} 0-float, 1-set PAD to high 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * Set input direction: 18162306a36Sopenharmony_ci * {mode1,mode0} = {0,0} 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistatic int mlxbf2_gpio_direction_input(struct gpio_chip *chip, 18462306a36Sopenharmony_ci unsigned int offset) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct mlxbf2_gpio_context *gs = gpiochip_get_data(chip); 18762306a36Sopenharmony_ci int ret; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * Although the arm_gpio_lock was set in the probe function, check again 19162306a36Sopenharmony_ci * if it is still enabled to be able to write to the ModeX registers. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci ret = mlxbf2_gpio_lock_acquire(gs); 19462306a36Sopenharmony_ci if (ret < 0) 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE0_CLEAR); 19862306a36Sopenharmony_ci writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE1_CLEAR); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci mlxbf2_gpio_lock_release(gs); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return ret; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* 20662306a36Sopenharmony_ci * Set output direction: 20762306a36Sopenharmony_ci * {mode1,mode0} = {0,1} 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_cistatic int mlxbf2_gpio_direction_output(struct gpio_chip *chip, 21062306a36Sopenharmony_ci unsigned int offset, 21162306a36Sopenharmony_ci int value) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct mlxbf2_gpio_context *gs = gpiochip_get_data(chip); 21462306a36Sopenharmony_ci int ret = 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * Although the arm_gpio_lock was set in the probe function, 21862306a36Sopenharmony_ci * check again it is still enabled to be able to write to the 21962306a36Sopenharmony_ci * ModeX registers. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci ret = mlxbf2_gpio_lock_acquire(gs); 22262306a36Sopenharmony_ci if (ret < 0) 22362306a36Sopenharmony_ci return ret; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE1_CLEAR); 22662306a36Sopenharmony_ci writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE0_SET); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci mlxbf2_gpio_lock_release(gs); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return ret; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void mlxbf2_gpio_irq_enable(struct irq_data *irqd) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); 23662306a36Sopenharmony_ci struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); 23762306a36Sopenharmony_ci int offset = irqd_to_hwirq(irqd); 23862306a36Sopenharmony_ci unsigned long flags; 23962306a36Sopenharmony_ci u32 val; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci gpiochip_enable_irq(gc, irqd_to_hwirq(irqd)); 24262306a36Sopenharmony_ci raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags); 24362306a36Sopenharmony_ci val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); 24462306a36Sopenharmony_ci val |= BIT(offset); 24562306a36Sopenharmony_ci writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); 24862306a36Sopenharmony_ci val |= BIT(offset); 24962306a36Sopenharmony_ci writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); 25062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void mlxbf2_gpio_irq_disable(struct irq_data *irqd) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); 25662306a36Sopenharmony_ci struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); 25762306a36Sopenharmony_ci int offset = irqd_to_hwirq(irqd); 25862306a36Sopenharmony_ci unsigned long flags; 25962306a36Sopenharmony_ci u32 val; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags); 26262306a36Sopenharmony_ci val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); 26362306a36Sopenharmony_ci val &= ~BIT(offset); 26462306a36Sopenharmony_ci writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); 26562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); 26662306a36Sopenharmony_ci gpiochip_disable_irq(gc, irqd_to_hwirq(irqd)); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic irqreturn_t mlxbf2_gpio_irq_handler(int irq, void *ptr) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct mlxbf2_gpio_context *gs = ptr; 27262306a36Sopenharmony_ci struct gpio_chip *gc = &gs->gc; 27362306a36Sopenharmony_ci unsigned long pending; 27462306a36Sopenharmony_ci u32 level; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci pending = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CAUSE_EVTEN0); 27762306a36Sopenharmony_ci writel(pending, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci for_each_set_bit(level, &pending, gc->ngpio) 28062306a36Sopenharmony_ci generic_handle_domain_irq_safe(gc->irq.domain, level); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return IRQ_RETVAL(pending); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int 28662306a36Sopenharmony_cimlxbf2_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); 28962306a36Sopenharmony_ci struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); 29062306a36Sopenharmony_ci int offset = irqd_to_hwirq(irqd); 29162306a36Sopenharmony_ci unsigned long flags; 29262306a36Sopenharmony_ci bool fall = false; 29362306a36Sopenharmony_ci bool rise = false; 29462306a36Sopenharmony_ci u32 val; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci switch (type & IRQ_TYPE_SENSE_MASK) { 29762306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 29862306a36Sopenharmony_ci fall = true; 29962306a36Sopenharmony_ci rise = true; 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 30262306a36Sopenharmony_ci rise = true; 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 30562306a36Sopenharmony_ci fall = true; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci default: 30862306a36Sopenharmony_ci return -EINVAL; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci raw_spin_lock_irqsave(&gs->gc.bgpio_lock, flags); 31262306a36Sopenharmony_ci if (fall) { 31362306a36Sopenharmony_ci val = readl(gs->gpio_io + YU_GPIO_CAUSE_FALL_EN); 31462306a36Sopenharmony_ci val |= BIT(offset); 31562306a36Sopenharmony_ci writel(val, gs->gpio_io + YU_GPIO_CAUSE_FALL_EN); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (rise) { 31962306a36Sopenharmony_ci val = readl(gs->gpio_io + YU_GPIO_CAUSE_RISE_EN); 32062306a36Sopenharmony_ci val |= BIT(offset); 32162306a36Sopenharmony_ci writel(val, gs->gpio_io + YU_GPIO_CAUSE_RISE_EN); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void mlxbf2_gpio_irq_print_chip(struct irq_data *irqd, 32962306a36Sopenharmony_ci struct seq_file *p) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); 33262306a36Sopenharmony_ci struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci seq_printf(p, dev_name(gs->dev)); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic const struct irq_chip mlxbf2_gpio_irq_chip = { 33862306a36Sopenharmony_ci .irq_set_type = mlxbf2_gpio_irq_set_type, 33962306a36Sopenharmony_ci .irq_enable = mlxbf2_gpio_irq_enable, 34062306a36Sopenharmony_ci .irq_disable = mlxbf2_gpio_irq_disable, 34162306a36Sopenharmony_ci .irq_print_chip = mlxbf2_gpio_irq_print_chip, 34262306a36Sopenharmony_ci .flags = IRQCHIP_IMMUTABLE, 34362306a36Sopenharmony_ci GPIOCHIP_IRQ_RESOURCE_HELPERS, 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/* BlueField-2 GPIO driver initialization routine. */ 34762306a36Sopenharmony_cistatic int 34862306a36Sopenharmony_cimlxbf2_gpio_probe(struct platform_device *pdev) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct mlxbf2_gpio_context *gs; 35162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 35262306a36Sopenharmony_ci struct gpio_irq_chip *girq; 35362306a36Sopenharmony_ci struct gpio_chip *gc; 35462306a36Sopenharmony_ci unsigned int npins; 35562306a36Sopenharmony_ci const char *name; 35662306a36Sopenharmony_ci int ret, irq; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci name = dev_name(dev); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL); 36162306a36Sopenharmony_ci if (!gs) 36262306a36Sopenharmony_ci return -ENOMEM; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci gs->dev = dev; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* YU GPIO block address */ 36762306a36Sopenharmony_ci gs->gpio_io = devm_platform_ioremap_resource(pdev, 0); 36862306a36Sopenharmony_ci if (IS_ERR(gs->gpio_io)) 36962306a36Sopenharmony_ci return PTR_ERR(gs->gpio_io); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ret = mlxbf2_gpio_get_lock_res(pdev); 37262306a36Sopenharmony_ci if (ret) { 37362306a36Sopenharmony_ci dev_err(dev, "Failed to get yu_arm_gpio_lock resource\n"); 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (device_property_read_u32(dev, "npins", &npins)) 37862306a36Sopenharmony_ci npins = MLXBF2_GPIO_MAX_PINS_PER_BLOCK; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci gc = &gs->gc; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ret = bgpio_init(gc, dev, 4, 38362306a36Sopenharmony_ci gs->gpio_io + YU_GPIO_DATAIN, 38462306a36Sopenharmony_ci gs->gpio_io + YU_GPIO_DATASET, 38562306a36Sopenharmony_ci gs->gpio_io + YU_GPIO_DATACLEAR, 38662306a36Sopenharmony_ci NULL, 38762306a36Sopenharmony_ci NULL, 38862306a36Sopenharmony_ci 0); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (ret) { 39162306a36Sopenharmony_ci dev_err(dev, "bgpio_init failed\n"); 39262306a36Sopenharmony_ci return ret; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci gc->direction_input = mlxbf2_gpio_direction_input; 39662306a36Sopenharmony_ci gc->direction_output = mlxbf2_gpio_direction_output; 39762306a36Sopenharmony_ci gc->ngpio = npins; 39862306a36Sopenharmony_ci gc->owner = THIS_MODULE; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 40162306a36Sopenharmony_ci if (irq >= 0) { 40262306a36Sopenharmony_ci girq = &gs->gc.irq; 40362306a36Sopenharmony_ci gpio_irq_chip_set_chip(girq, &mlxbf2_gpio_irq_chip); 40462306a36Sopenharmony_ci girq->handler = handle_simple_irq; 40562306a36Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 40662306a36Sopenharmony_ci /* This will let us handle the parent IRQ in the driver */ 40762306a36Sopenharmony_ci girq->num_parents = 0; 40862306a36Sopenharmony_ci girq->parents = NULL; 40962306a36Sopenharmony_ci girq->parent_handler = NULL; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* 41262306a36Sopenharmony_ci * Directly request the irq here instead of passing 41362306a36Sopenharmony_ci * a flow-handler because the irq is shared. 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, mlxbf2_gpio_irq_handler, 41662306a36Sopenharmony_ci IRQF_SHARED, name, gs); 41762306a36Sopenharmony_ci if (ret) { 41862306a36Sopenharmony_ci dev_err(dev, "failed to request IRQ"); 41962306a36Sopenharmony_ci return ret; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci platform_set_drvdata(pdev, gs); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci ret = devm_gpiochip_add_data(dev, &gs->gc, gs); 42662306a36Sopenharmony_ci if (ret) { 42762306a36Sopenharmony_ci dev_err(dev, "Failed adding memory mapped gpiochip\n"); 42862306a36Sopenharmony_ci return ret; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int __maybe_unused mlxbf2_gpio_suspend(struct device *dev) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci gs->csave_regs->gpio_mode0 = readl(gs->gpio_io + 43962306a36Sopenharmony_ci YU_GPIO_MODE0); 44062306a36Sopenharmony_ci gs->csave_regs->gpio_mode1 = readl(gs->gpio_io + 44162306a36Sopenharmony_ci YU_GPIO_MODE1); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int __maybe_unused mlxbf2_gpio_resume(struct device *dev) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci writel(gs->csave_regs->gpio_mode0, gs->gpio_io + 45162306a36Sopenharmony_ci YU_GPIO_MODE0); 45262306a36Sopenharmony_ci writel(gs->csave_regs->gpio_mode1, gs->gpio_io + 45362306a36Sopenharmony_ci YU_GPIO_MODE1); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return 0; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mlxbf2_pm_ops, mlxbf2_gpio_suspend, mlxbf2_gpio_resume); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = { 46062306a36Sopenharmony_ci { "MLNXBF22", 0 }, 46162306a36Sopenharmony_ci {}, 46262306a36Sopenharmony_ci}; 46362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mlxbf2_gpio_acpi_match); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic struct platform_driver mlxbf2_gpio_driver = { 46662306a36Sopenharmony_ci .driver = { 46762306a36Sopenharmony_ci .name = "mlxbf2_gpio", 46862306a36Sopenharmony_ci .acpi_match_table = mlxbf2_gpio_acpi_match, 46962306a36Sopenharmony_ci .pm = &mlxbf2_pm_ops, 47062306a36Sopenharmony_ci }, 47162306a36Sopenharmony_ci .probe = mlxbf2_gpio_probe, 47262306a36Sopenharmony_ci}; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cimodule_platform_driver(mlxbf2_gpio_driver); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciMODULE_DESCRIPTION("Mellanox BlueField-2 GPIO Driver"); 47762306a36Sopenharmony_ciMODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>"); 47862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 479