linux/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_gpio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
   2
   3/* Initialize and handle GPIO interrupt triggered by INT_N PHY signal.
   4 * This GPIO interrupt triggers the PHY state machine to bring the link
   5 * up/down.
   6 *
   7 * Copyright (C) 2021 NVIDIA CORPORATION & AFFILIATES
   8 */
   9
  10#include <linux/acpi.h>
  11#include <linux/bitfield.h>
  12#include <linux/device.h>
  13#include <linux/err.h>
  14#include <linux/gpio/driver.h>
  15#include <linux/interrupt.h>
  16#include <linux/io.h>
  17#include <linux/irq.h>
  18#include <linux/irqdomain.h>
  19#include <linux/irqreturn.h>
  20#include <linux/platform_device.h>
  21#include <linux/property.h>
  22
  23#include "mlxbf_gige.h"
  24#include "mlxbf_gige_regs.h"
  25
  26#define MLXBF_GIGE_GPIO_CAUSE_FALL_EN           0x48
  27#define MLXBF_GIGE_GPIO_CAUSE_OR_CAUSE_EVTEN0   0x80
  28#define MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0         0x94
  29#define MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE       0x98
  30
  31static void mlxbf_gige_gpio_enable(struct mlxbf_gige *priv)
  32{
  33        unsigned long flags;
  34        u32 val;
  35
  36        spin_lock_irqsave(&priv->gpio_lock, flags);
  37        val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE);
  38        val |= priv->phy_int_gpio_mask;
  39        writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE);
  40
  41        /* The INT_N interrupt level is active low.
  42         * So enable cause fall bit to detect when GPIO
  43         * state goes low.
  44         */
  45        val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_FALL_EN);
  46        val |= priv->phy_int_gpio_mask;
  47        writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_FALL_EN);
  48
  49        /* Enable PHY interrupt by setting the priority level */
  50        val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0);
  51        val |= priv->phy_int_gpio_mask;
  52        writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0);
  53        spin_unlock_irqrestore(&priv->gpio_lock, flags);
  54}
  55
  56static void mlxbf_gige_gpio_disable(struct mlxbf_gige *priv)
  57{
  58        unsigned long flags;
  59        u32 val;
  60
  61        spin_lock_irqsave(&priv->gpio_lock, flags);
  62        val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0);
  63        val &= ~priv->phy_int_gpio_mask;
  64        writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0);
  65        spin_unlock_irqrestore(&priv->gpio_lock, flags);
  66}
  67
  68static irqreturn_t mlxbf_gige_gpio_handler(int irq, void *ptr)
  69{
  70        struct mlxbf_gige *priv;
  71        u32 val;
  72
  73        priv = ptr;
  74
  75        /* Check if this interrupt is from PHY device.
  76         * Return if it is not.
  77         */
  78        val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CAUSE_EVTEN0);
  79        if (!(val & priv->phy_int_gpio_mask))
  80                return IRQ_NONE;
  81
  82        /* Clear interrupt when done, otherwise, no further interrupt
  83         * will be triggered.
  84         */
  85        val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE);
  86        val |= priv->phy_int_gpio_mask;
  87        writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE);
  88
  89        generic_handle_irq(priv->phy_irq);
  90
  91        return IRQ_HANDLED;
  92}
  93
  94static void mlxbf_gige_gpio_mask(struct irq_data *irqd)
  95{
  96        struct mlxbf_gige *priv = irq_data_get_irq_chip_data(irqd);
  97
  98        mlxbf_gige_gpio_disable(priv);
  99}
 100
 101static void mlxbf_gige_gpio_unmask(struct irq_data *irqd)
 102{
 103        struct mlxbf_gige *priv = irq_data_get_irq_chip_data(irqd);
 104
 105        mlxbf_gige_gpio_enable(priv);
 106}
 107
 108static struct irq_chip mlxbf_gige_gpio_chip = {
 109        .name                   = "mlxbf_gige_phy",
 110        .irq_mask               = mlxbf_gige_gpio_mask,
 111        .irq_unmask             = mlxbf_gige_gpio_unmask,
 112};
 113
 114static int mlxbf_gige_gpio_domain_map(struct irq_domain *d,
 115                                      unsigned int irq,
 116                                      irq_hw_number_t hwirq)
 117{
 118        irq_set_chip_data(irq, d->host_data);
 119        irq_set_chip_and_handler(irq, &mlxbf_gige_gpio_chip, handle_simple_irq);
 120        irq_set_noprobe(irq);
 121
 122        return 0;
 123}
 124
 125static const struct irq_domain_ops mlxbf_gige_gpio_domain_ops = {
 126        .map    = mlxbf_gige_gpio_domain_map,
 127        .xlate  = irq_domain_xlate_twocell,
 128};
 129
 130#ifdef CONFIG_ACPI
 131static int mlxbf_gige_gpio_resources(struct acpi_resource *ares,
 132                                     void *data)
 133{
 134        struct acpi_resource_gpio *gpio;
 135        u32 *phy_int_gpio = data;
 136
 137        if (ares->type == ACPI_RESOURCE_TYPE_GPIO) {
 138                gpio = &ares->data.gpio;
 139                *phy_int_gpio = gpio->pin_table[0];
 140        }
 141
 142        return 1;
 143}
 144#endif
 145
 146void mlxbf_gige_gpio_free(struct mlxbf_gige *priv)
 147{
 148        irq_dispose_mapping(priv->phy_irq);
 149        irq_domain_remove(priv->irqdomain);
 150}
 151
 152int mlxbf_gige_gpio_init(struct platform_device *pdev,
 153                         struct mlxbf_gige *priv)
 154{
 155        struct device *dev = &pdev->dev;
 156        struct resource *res;
 157        u32 phy_int_gpio = 0;
 158        int ret;
 159
 160        LIST_HEAD(resources);
 161
 162        res = platform_get_resource(pdev, IORESOURCE_MEM, MLXBF_GIGE_RES_GPIO0);
 163        if (!res)
 164                return -ENODEV;
 165
 166        priv->gpio_io = devm_ioremap(dev, res->start, resource_size(res));
 167        if (!priv->gpio_io)
 168                return -ENOMEM;
 169
 170#ifdef CONFIG_ACPI
 171        ret = acpi_dev_get_resources(ACPI_COMPANION(dev),
 172                                     &resources, mlxbf_gige_gpio_resources,
 173                                     &phy_int_gpio);
 174        acpi_dev_free_resource_list(&resources);
 175        if (ret < 0 || !phy_int_gpio) {
 176                dev_err(dev, "Error retrieving the gpio phy pin");
 177                return -EINVAL;
 178        }
 179#endif
 180
 181        priv->phy_int_gpio_mask = BIT(phy_int_gpio);
 182
 183        mlxbf_gige_gpio_disable(priv);
 184
 185        priv->hw_phy_irq = platform_get_irq(pdev, MLXBF_GIGE_PHY_INT_N);
 186
 187        priv->irqdomain = irq_domain_add_simple(NULL, 1, 0,
 188                                                &mlxbf_gige_gpio_domain_ops,
 189                                                priv);
 190        if (!priv->irqdomain) {
 191                dev_err(dev, "Failed to add IRQ domain\n");
 192                return -ENOMEM;
 193        }
 194
 195        priv->phy_irq = irq_create_mapping(priv->irqdomain, 0);
 196        if (!priv->phy_irq) {
 197                irq_domain_remove(priv->irqdomain);
 198                priv->irqdomain = NULL;
 199                dev_err(dev, "Error mapping PHY IRQ\n");
 200                return -EINVAL;
 201        }
 202
 203        ret = devm_request_irq(dev, priv->hw_phy_irq, mlxbf_gige_gpio_handler,
 204                               IRQF_ONESHOT | IRQF_SHARED, "mlxbf_gige_phy", priv);
 205        if (ret) {
 206                dev_err(dev, "Failed to request PHY IRQ");
 207                mlxbf_gige_gpio_free(priv);
 208                return ret;
 209        }
 210
 211        return ret;
 212}
 213