123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- // 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/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 0xd42f is written to this field
- */
- #define YU_ARM_GPIO_LOCK_ADDR 0x2801088
- #define YU_ARM_GPIO_LOCK_SIZE 0x8
- #define YU_LOCK_ACTIVE_BIT(val) (val >> 31)
- #define YU_ARM_GPIO_LOCK_ACQUIRE 0xd42f
- #define YU_ARM_GPIO_LOCK_RELEASE 0x0
- /*
- * gpio[x] block registers and their offset
- */
- #define YU_GPIO_DATAIN 0x04
- #define YU_GPIO_MODE1 0x08
- #define YU_GPIO_MODE0 0x0c
- #define YU_GPIO_DATASET 0x14
- #define YU_GPIO_DATACLEAR 0x18
- #define YU_GPIO_CAUSE_RISE_EN 0x44
- #define YU_GPIO_CAUSE_FALL_EN 0x48
- #define YU_GPIO_MODE1_CLEAR 0x50
- #define YU_GPIO_MODE0_SET 0x54
- #define YU_GPIO_MODE0_CLEAR 0x58
- #define YU_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x80
- #define YU_GPIO_CAUSE_OR_EVTEN0 0x94
- #define YU_GPIO_CAUSE_OR_CLRCAUSE 0x98
- 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;
- struct irq_chip irq_chip;
- /* YU GPIO blocks address */
- void __iomem *gpio_io;
- 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;
- 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);
- }
- 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;
- }
- /* 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;
- /* 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(pdev, 0);
- if (irq >= 0) {
- gs->irq_chip.name = name;
- gs->irq_chip.irq_set_type = mlxbf2_gpio_irq_set_type;
- gs->irq_chip.irq_enable = mlxbf2_gpio_irq_enable;
- gs->irq_chip.irq_disable = mlxbf2_gpio_irq_disable;
- girq = &gs->gc.irq;
- girq->chip = &gs->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 <[email protected]>");
- MODULE_LICENSE("GPL v2");
|