Commit f0967377 authored by Bartosz Golaszewski's avatar Bartosz Golaszewski

Merge tag 'intel-gpio-v6.4-2' of...

Merge tag 'intel-gpio-v6.4-2' of git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel into gpio/for-next

intel-gpio for v6.4-2

* Fixed suspend issue on Clevo NL5xNU
* Split a new Intel Tangier (library) driver for current and new platforms
* Introduced a new driver for Intel Elkhart Lake PSE GPIO (see also above)
* Contained a few fixes for the previous of_gpio.h cleanup
* Miscellaneous cleanups here and there

The following is an automated git shortlog grouped by driver:

elkhartlake:
 -  Introduce Intel Elkhart Lake PSE GPIO

gpiolib:
 -  acpi: Add a ignore wakeup quirk for Clevo NL5xNU
 -  acpi: Move ACPI device NULL check to acpi_get_driver_gpio_data()
 -  acpi: use the fwnode in acpi_gpiochip_find()

ich:
 -  Use devm_gpiochip_add_data() to simplify remove path

merrifield:
 -  Utilise temporary variable for struct device
 -  Use dev_err_probe()
 -  Adapt to Intel Tangier GPIO driver

mips:
 -  ar7: include linux/gpio/driver.h

mm-lantiq:
 -  Fix typo in the newly added header filename

powerpc/40x:
 -  Add missing select OF_GPIO_MM_GPIOCHIP

sh:
 -  mach-x3proto: Add missing #include <linux/gpio/driver.h>

tangier:
 -  Introduce Intel Tangier GPIO driver
parents 7b59bdbc 782eea0c
...@@ -10281,12 +10281,14 @@ M: Andy Shevchenko <andy@kernel.org> ...@@ -10281,12 +10281,14 @@ M: Andy Shevchenko <andy@kernel.org>
L: linux-gpio@vger.kernel.org L: linux-gpio@vger.kernel.org
S: Supported S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel.git
F: drivers/gpio/gpio-elkhartlake.c
F: drivers/gpio/gpio-ich.c F: drivers/gpio/gpio-ich.c
F: drivers/gpio/gpio-merrifield.c F: drivers/gpio/gpio-merrifield.c
F: drivers/gpio/gpio-ml-ioh.c F: drivers/gpio/gpio-ml-ioh.c
F: drivers/gpio/gpio-pch.c F: drivers/gpio/gpio-pch.c
F: drivers/gpio/gpio-sch.c F: drivers/gpio/gpio-sch.c
F: drivers/gpio/gpio-sodaville.c F: drivers/gpio/gpio-sodaville.c
F: drivers/gpio/gpio-tangier.c
INTEL GVT-g DRIVERS (Intel GPU Virtualization) INTEL GVT-g DRIVERS (Intel GPU Virtualization)
M: Zhenyu Wang <zhenyuw@linux.intel.com> M: Zhenyu Wang <zhenyuw@linux.intel.com>
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/gpio.h> #include <linux/gpio/driver.h>
#include <asm/mach-ar7/ar7.h> #include <asm/mach-ar7/ar7.h>
......
...@@ -65,6 +65,7 @@ config PPC4xx_GPIO ...@@ -65,6 +65,7 @@ config PPC4xx_GPIO
bool "PPC4xx GPIO support" bool "PPC4xx GPIO support"
depends on 40x depends on 40x
select GPIOLIB select GPIOLIB
select OF_GPIO_MM_GPIOCHIP
help help
Enable gpiolib support for ppc40x based boards Enable gpiolib support for ppc40x based boards
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/usb/r8a66597.h> #include <linux/usb/r8a66597.h>
#include <linux/usb/m66592.h> #include <linux/usb/m66592.h>
#include <linux/gpio.h> #include <linux/gpio/driver.h>
#include <linux/gpio_keys.h> #include <linux/gpio_keys.h>
#include <mach/ilsel.h> #include <mach/ilsel.h>
#include <mach/hardware.h> #include <mach/hardware.h>
......
...@@ -623,6 +623,17 @@ config GPIO_SYSCON ...@@ -623,6 +623,17 @@ config GPIO_SYSCON
help help
Say yes here to support GPIO functionality though SYSCON driver. Say yes here to support GPIO functionality though SYSCON driver.
config GPIO_TANGIER
tristate
select GPIOLIB_IRQCHIP
help
GPIO support for Intel Tangier and compatible platforms.
Currently supported:
- Elkhart Lake
- Merrifield
If built as a module its name will be gpio-tangier.
config GPIO_TB10X config GPIO_TB10X
bool bool
select GPIO_GENERIC select GPIO_GENERIC
...@@ -1252,6 +1263,17 @@ config HTC_EGPIO ...@@ -1252,6 +1263,17 @@ config HTC_EGPIO
several HTC phones. It provides basic support for input several HTC phones. It provides basic support for input
pins, output pins, and IRQs. pins, output pins, and IRQs.
config GPIO_ELKHARTLAKE
tristate "Intel Elkhart Lake PSE GPIO support"
depends on X86 || COMPILE_TEST
select GPIO_TANGIER
help
Select this option to enable GPIO support for Intel Elkhart Lake
PSE GPIO IP.
To compile this driver as a module, choose M here: the module will
be called gpio-elkhartlake.
config GPIO_JANZ_TTL config GPIO_JANZ_TTL
tristate "Janz VMOD-TTL Digital IO Module" tristate "Janz VMOD-TTL Digital IO Module"
depends on MFD_JANZ_CMODIO depends on MFD_JANZ_CMODIO
...@@ -1530,7 +1552,7 @@ config GPIO_BT8XX ...@@ -1530,7 +1552,7 @@ config GPIO_BT8XX
config GPIO_MERRIFIELD config GPIO_MERRIFIELD
tristate "Intel Merrifield GPIO support" tristate "Intel Merrifield GPIO support"
depends on X86_INTEL_MID depends on X86_INTEL_MID
select GPIOLIB_IRQCHIP select GPIO_TANGIER
help help
Say Y here to support Intel Merrifield GPIO. Say Y here to support Intel Merrifield GPIO.
......
...@@ -54,6 +54,7 @@ obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o ...@@ -54,6 +54,7 @@ obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o
obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o
obj-$(CONFIG_GPIO_EIC_SPRD) += gpio-eic-sprd.o obj-$(CONFIG_GPIO_EIC_SPRD) += gpio-eic-sprd.o
obj-$(CONFIG_GPIO_ELKHARTLAKE) += gpio-elkhartlake.o
obj-$(CONFIG_GPIO_EM) += gpio-em.o obj-$(CONFIG_GPIO_EM) += gpio-em.o
obj-$(CONFIG_GPIO_EN7523) += gpio-en7523.o obj-$(CONFIG_GPIO_EN7523) += gpio-en7523.o
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
...@@ -147,6 +148,7 @@ obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o ...@@ -147,6 +148,7 @@ obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
obj-$(CONFIG_GPIO_TANGIER) += gpio-tangier.o
obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Elkhart Lake PSE GPIO driver
*
* Copyright (c) 2023 Intel Corporation.
*
* Authors: Pandith N <pandith.n@intel.com>
* Raag Jadav <raag.jadav@intel.com>
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include "gpio-tangier.h"
/* Each Intel EHL PSE GPIO Controller has 30 GPIO pins */
#define EHL_PSE_NGPIO 30
static int ehl_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tng_gpio *priv;
int irq, ret;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->reg_base))
return PTR_ERR(priv->reg_base);
priv->dev = dev;
priv->irq = irq;
priv->info.base = -1;
priv->info.ngpio = EHL_PSE_NGPIO;
priv->wake_regs.gwmr = GWMR_EHL;
priv->wake_regs.gwsr = GWSR_EHL;
priv->wake_regs.gsir = GSIR_EHL;
ret = devm_tng_gpio_probe(dev, priv);
if (ret)
return dev_err_probe(dev, ret, "tng_gpio_probe error\n");
platform_set_drvdata(pdev, priv);
return 0;
}
static int ehl_gpio_suspend(struct device *dev)
{
return tng_gpio_suspend(dev);
}
static int ehl_gpio_resume(struct device *dev)
{
return tng_gpio_resume(dev);
}
static DEFINE_SIMPLE_DEV_PM_OPS(ehl_gpio_pm_ops, ehl_gpio_suspend, ehl_gpio_resume);
static const struct platform_device_id ehl_gpio_ids[] = {
{ "gpio-elkhartlake" },
{ }
};
MODULE_DEVICE_TABLE(platform, ehl_gpio_ids);
static struct platform_driver ehl_gpio_driver = {
.driver = {
.name = "gpio-elkhartlake",
.pm = pm_sleep_ptr(&ehl_gpio_pm_ops),
},
.probe = ehl_gpio_probe,
.id_table = ehl_gpio_ids,
};
module_platform_driver(ehl_gpio_driver);
MODULE_AUTHOR("Pandith N <pandith.n@intel.com>");
MODULE_AUTHOR("Raag Jadav <raag.jadav@intel.com>");
MODULE_DESCRIPTION("Intel Elkhart Lake PSE GPIO driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(GPIO_TANGIER);
...@@ -457,7 +457,7 @@ static int ichx_gpio_probe(struct platform_device *pdev) ...@@ -457,7 +457,7 @@ static int ichx_gpio_probe(struct platform_device *pdev)
init: init:
ichx_gpiolib_setup(&ichx_priv.chip); ichx_gpiolib_setup(&ichx_priv.chip);
err = gpiochip_add_data(&ichx_priv.chip, NULL); err = devm_gpiochip_add_data(dev, &ichx_priv.chip, NULL);
if (err) { if (err) {
dev_err(dev, "Failed to register GPIOs\n"); dev_err(dev, "Failed to register GPIOs\n");
return err; return err;
...@@ -469,19 +469,11 @@ static int ichx_gpio_probe(struct platform_device *pdev) ...@@ -469,19 +469,11 @@ static int ichx_gpio_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int ichx_gpio_remove(struct platform_device *pdev)
{
gpiochip_remove(&ichx_priv.chip);
return 0;
}
static struct platform_driver ichx_gpio_driver = { static struct platform_driver ichx_gpio_driver = {
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
}, },
.probe = ichx_gpio_probe, .probe = ichx_gpio_probe,
.remove = ichx_gpio_remove,
}; };
module_platform_driver(ichx_gpio_driver); module_platform_driver(ichx_gpio_driver);
......
...@@ -2,60 +2,25 @@ ...@@ -2,60 +2,25 @@
/* /*
* Intel Merrifield SoC GPIO driver * Intel Merrifield SoC GPIO driver
* *
* Copyright (c) 2016 Intel Corporation. * Copyright (c) 2016, 2023 Intel Corporation.
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
*/ */
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/gpio/driver.h> #include <linux/device.h>
#include <linux/interrupt.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pinctrl/consumer.h> #include <linux/types.h>
#include <linux/string_helpers.h>
#define GCCR 0x000 /* controller configuration */ #include "gpio-tangier.h"
#define GPLR 0x004 /* pin level r/o */
#define GPDR 0x01c /* pin direction */
#define GPSR 0x034 /* pin set w/o */
#define GPCR 0x04c /* pin clear w/o */
#define GRER 0x064 /* rising edge detect */
#define GFER 0x07c /* falling edge detect */
#define GFBR 0x094 /* glitch filter bypass */
#define GIMR 0x0ac /* interrupt mask */
#define GISR 0x0c4 /* interrupt source */
#define GITR 0x300 /* input type */
#define GLPR 0x318 /* level input polarity */
#define GWMR 0x400 /* wake mask */
#define GWSR 0x418 /* wake source */
#define GSIR 0xc00 /* secure input */
/* Intel Merrifield has 192 GPIO pins */ /* Intel Merrifield has 192 GPIO pins */
#define MRFLD_NGPIO 192 #define MRFLD_NGPIO 192
struct mrfld_gpio_pinrange { static const struct tng_gpio_pinrange mrfld_gpio_ranges[] = {
unsigned int gpio_base;
unsigned int pin_base;
unsigned int npins;
};
#define GPIO_PINRANGE(gstart, gend, pstart) \
{ \
.gpio_base = (gstart), \
.pin_base = (pstart), \
.npins = (gend) - (gstart) + 1, \
}
struct mrfld_gpio {
struct gpio_chip chip;
void __iomem *reg_base;
raw_spinlock_t lock;
struct device *dev;
};
static const struct mrfld_gpio_pinrange mrfld_gpio_ranges[] = {
GPIO_PINRANGE(0, 11, 146), GPIO_PINRANGE(0, 11, 146),
GPIO_PINRANGE(12, 13, 144), GPIO_PINRANGE(12, 13, 144),
GPIO_PINRANGE(14, 15, 35), GPIO_PINRANGE(14, 15, 35),
...@@ -84,323 +49,15 @@ static const struct mrfld_gpio_pinrange mrfld_gpio_ranges[] = { ...@@ -84,323 +49,15 @@ static const struct mrfld_gpio_pinrange mrfld_gpio_ranges[] = {
GPIO_PINRANGE(190, 191, 178), GPIO_PINRANGE(190, 191, 178),
}; };
static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned int offset, static const char *mrfld_gpio_get_pinctrl_dev_name(struct tng_gpio *priv)
unsigned int reg_type_offset)
{
struct mrfld_gpio *priv = gpiochip_get_data(chip);
u8 reg = offset / 32;
return priv->reg_base + reg_type_offset + reg * 4;
}
static int mrfld_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
void __iomem *gplr = gpio_reg(chip, offset, GPLR);
return !!(readl(gplr) & BIT(offset % 32));
}
static void mrfld_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct mrfld_gpio *priv = gpiochip_get_data(chip);
void __iomem *gpsr, *gpcr;
unsigned long flags;
raw_spin_lock_irqsave(&priv->lock, flags);
if (value) {
gpsr = gpio_reg(chip, offset, GPSR);
writel(BIT(offset % 32), gpsr);
} else {
gpcr = gpio_reg(chip, offset, GPCR);
writel(BIT(offset % 32), gpcr);
}
raw_spin_unlock_irqrestore(&priv->lock, flags);
}
static int mrfld_gpio_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
struct mrfld_gpio *priv = gpiochip_get_data(chip);
void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
unsigned long flags;
u32 value;
raw_spin_lock_irqsave(&priv->lock, flags);
value = readl(gpdr);
value &= ~BIT(offset % 32);
writel(value, gpdr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int mrfld_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct mrfld_gpio *priv = gpiochip_get_data(chip);
void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
unsigned long flags;
mrfld_gpio_set(chip, offset, value);
raw_spin_lock_irqsave(&priv->lock, flags);
value = readl(gpdr);
value |= BIT(offset % 32);
writel(value, gpdr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int mrfld_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
if (readl(gpdr) & BIT(offset % 32))
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
}
static int mrfld_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset,
unsigned int debounce)
{
struct mrfld_gpio *priv = gpiochip_get_data(chip);
void __iomem *gfbr = gpio_reg(chip, offset, GFBR);
unsigned long flags;
u32 value;
raw_spin_lock_irqsave(&priv->lock, flags);
if (debounce)
value = readl(gfbr) & ~BIT(offset % 32);
else
value = readl(gfbr) | BIT(offset % 32);
writel(value, gfbr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int mrfld_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
unsigned long config)
{
u32 debounce;
if ((pinconf_to_config_param(config) == PIN_CONFIG_BIAS_DISABLE) ||
(pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_UP) ||
(pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_DOWN))
return gpiochip_generic_config(chip, offset, config);
if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
return -ENOTSUPP;
debounce = pinconf_to_config_argument(config);
return mrfld_gpio_set_debounce(chip, offset, debounce);
}
static void mrfld_irq_ack(struct irq_data *d)
{
struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d);
u32 gpio = irqd_to_hwirq(d);
void __iomem *gisr = gpio_reg(&priv->chip, gpio, GISR);
unsigned long flags;
raw_spin_lock_irqsave(&priv->lock, flags);
writel(BIT(gpio % 32), gisr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
}
static void mrfld_irq_unmask_mask(struct mrfld_gpio *priv, u32 gpio, bool unmask)
{
void __iomem *gimr = gpio_reg(&priv->chip, gpio, GIMR);
unsigned long flags;
u32 value;
raw_spin_lock_irqsave(&priv->lock, flags);
if (unmask)
value = readl(gimr) | BIT(gpio % 32);
else
value = readl(gimr) & ~BIT(gpio % 32);
writel(value, gimr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
}
static void mrfld_irq_mask(struct irq_data *d)
{
struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d);
u32 gpio = irqd_to_hwirq(d);
mrfld_irq_unmask_mask(priv, gpio, false);
gpiochip_disable_irq(&priv->chip, gpio);
}
static void mrfld_irq_unmask(struct irq_data *d)
{
struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d);
u32 gpio = irqd_to_hwirq(d);
gpiochip_enable_irq(&priv->chip, gpio);
mrfld_irq_unmask_mask(priv, gpio, true);
}
static int mrfld_irq_set_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mrfld_gpio *priv = gpiochip_get_data(gc);
u32 gpio = irqd_to_hwirq(d);
void __iomem *grer = gpio_reg(&priv->chip, gpio, GRER);
void __iomem *gfer = gpio_reg(&priv->chip, gpio, GFER);
void __iomem *gitr = gpio_reg(&priv->chip, gpio, GITR);
void __iomem *glpr = gpio_reg(&priv->chip, gpio, GLPR);
unsigned long flags;
u32 value;
raw_spin_lock_irqsave(&priv->lock, flags);
if (type & IRQ_TYPE_EDGE_RISING)
value = readl(grer) | BIT(gpio % 32);
else
value = readl(grer) & ~BIT(gpio % 32);
writel(value, grer);
if (type & IRQ_TYPE_EDGE_FALLING)
value = readl(gfer) | BIT(gpio % 32);
else
value = readl(gfer) & ~BIT(gpio % 32);
writel(value, gfer);
/*
* To prevent glitches from triggering an unintended level interrupt,
* configure GLPR register first and then configure GITR.
*/
if (type & IRQ_TYPE_LEVEL_LOW)
value = readl(glpr) | BIT(gpio % 32);
else
value = readl(glpr) & ~BIT(gpio % 32);
writel(value, glpr);
if (type & IRQ_TYPE_LEVEL_MASK) {
value = readl(gitr) | BIT(gpio % 32);
writel(value, gitr);
irq_set_handler_locked(d, handle_level_irq);
} else if (type & IRQ_TYPE_EDGE_BOTH) {
value = readl(gitr) & ~BIT(gpio % 32);
writel(value, gitr);
irq_set_handler_locked(d, handle_edge_irq);
}
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int mrfld_irq_set_wake(struct irq_data *d, unsigned int on)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mrfld_gpio *priv = gpiochip_get_data(gc);
u32 gpio = irqd_to_hwirq(d);
void __iomem *gwmr = gpio_reg(&priv->chip, gpio, GWMR);
void __iomem *gwsr = gpio_reg(&priv->chip, gpio, GWSR);
unsigned long flags;
u32 value;
raw_spin_lock_irqsave(&priv->lock, flags);
/* Clear the existing wake status */
writel(BIT(gpio % 32), gwsr);
if (on)
value = readl(gwmr) | BIT(gpio % 32);
else
value = readl(gwmr) & ~BIT(gpio % 32);
writel(value, gwmr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
dev_dbg(priv->dev, "%s wake for gpio %u\n", str_enable_disable(on), gpio);
return 0;
}
static const struct irq_chip mrfld_irqchip = {
.name = "gpio-merrifield",
.irq_ack = mrfld_irq_ack,
.irq_mask = mrfld_irq_mask,
.irq_unmask = mrfld_irq_unmask,
.irq_set_type = mrfld_irq_set_type,
.irq_set_wake = mrfld_irq_set_wake,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static void mrfld_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct mrfld_gpio *priv = gpiochip_get_data(gc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
unsigned long base, gpio;
chained_irq_enter(irqchip, desc);
/* Check GPIO controller to check which pin triggered the interrupt */
for (base = 0; base < priv->chip.ngpio; base += 32) {
void __iomem *gisr = gpio_reg(&priv->chip, base, GISR);
void __iomem *gimr = gpio_reg(&priv->chip, base, GIMR);
unsigned long pending, enabled;
pending = readl(gisr);
enabled = readl(gimr);
/* Only interrupts that are enabled */
pending &= enabled;
for_each_set_bit(gpio, &pending, 32)
generic_handle_domain_irq(gc->irq.domain, base + gpio);
}
chained_irq_exit(irqchip, desc);
}
static int mrfld_irq_init_hw(struct gpio_chip *chip)
{
struct mrfld_gpio *priv = gpiochip_get_data(chip);
void __iomem *reg;
unsigned int base;
for (base = 0; base < priv->chip.ngpio; base += 32) {
/* Clear the rising-edge detect register */
reg = gpio_reg(&priv->chip, base, GRER);
writel(0, reg);
/* Clear the falling-edge detect register */
reg = gpio_reg(&priv->chip, base, GFER);
writel(0, reg);
}
return 0;
}
static const char *mrfld_gpio_get_pinctrl_dev_name(struct mrfld_gpio *priv)
{ {
struct device *dev = priv->dev;
struct acpi_device *adev; struct acpi_device *adev;
const char *name; const char *name;
adev = acpi_dev_get_first_match_dev("INTC1002", NULL, -1); adev = acpi_dev_get_first_match_dev("INTC1002", NULL, -1);
if (adev) { if (adev) {
name = devm_kstrdup(priv->dev, acpi_dev_name(adev), GFP_KERNEL); name = devm_kstrdup(dev, acpi_dev_name(adev), GFP_KERNEL);
acpi_dev_put(adev); acpi_dev_put(adev);
} else { } else {
name = "pinctrl-merrifield"; name = "pinctrl-merrifield";
...@@ -409,37 +66,10 @@ static const char *mrfld_gpio_get_pinctrl_dev_name(struct mrfld_gpio *priv) ...@@ -409,37 +66,10 @@ static const char *mrfld_gpio_get_pinctrl_dev_name(struct mrfld_gpio *priv)
return name; return name;
} }
static int mrfld_gpio_add_pin_ranges(struct gpio_chip *chip)
{
struct mrfld_gpio *priv = gpiochip_get_data(chip);
const struct mrfld_gpio_pinrange *range;
const char *pinctrl_dev_name;
unsigned int i;
int retval;
pinctrl_dev_name = mrfld_gpio_get_pinctrl_dev_name(priv);
if (!pinctrl_dev_name)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) {
range = &mrfld_gpio_ranges[i];
retval = gpiochip_add_pin_range(&priv->chip, pinctrl_dev_name,
range->gpio_base,
range->pin_base,
range->npins);
if (retval) {
dev_err(priv->dev, "failed to add GPIO pin range\n");
return retval;
}
}
return 0;
}
static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id) static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ {
struct gpio_irq_chip *girq; struct device *dev = &pdev->dev;
struct mrfld_gpio *priv; struct tng_gpio *priv;
u32 gpio_base, irq_base; u32 gpio_base, irq_base;
void __iomem *base; void __iomem *base;
int retval; int retval;
...@@ -449,10 +79,8 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id ...@@ -449,10 +79,8 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
return retval; return retval;
retval = pcim_iomap_regions(pdev, BIT(1) | BIT(0), pci_name(pdev)); retval = pcim_iomap_regions(pdev, BIT(1) | BIT(0), pci_name(pdev));
if (retval) { if (retval)
dev_err(&pdev->dev, "I/O memory mapping error\n"); return dev_err_probe(dev, retval, "I/O memory mapping error\n");
return retval;
}
base = pcim_iomap_table(pdev)[1]; base = pcim_iomap_table(pdev)[1];
...@@ -462,53 +90,36 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id ...@@ -462,53 +90,36 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
/* Release the IO mapping, since we already get the info from BAR1 */ /* Release the IO mapping, since we already get the info from BAR1 */
pcim_iounmap_regions(pdev, BIT(1)); pcim_iounmap_regions(pdev, BIT(1));
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
priv->dev = &pdev->dev; priv->dev = dev;
priv->reg_base = pcim_iomap_table(pdev)[0]; priv->reg_base = pcim_iomap_table(pdev)[0];
priv->chip.label = dev_name(&pdev->dev); priv->pin_info.pin_ranges = mrfld_gpio_ranges;
priv->chip.parent = &pdev->dev; priv->pin_info.nranges = ARRAY_SIZE(mrfld_gpio_ranges);
priv->chip.request = gpiochip_generic_request; priv->pin_info.name = mrfld_gpio_get_pinctrl_dev_name(priv);
priv->chip.free = gpiochip_generic_free; if (!priv->pin_info.name)
priv->chip.direction_input = mrfld_gpio_direction_input; return -ENOMEM;
priv->chip.direction_output = mrfld_gpio_direction_output;
priv->chip.get = mrfld_gpio_get;
priv->chip.set = mrfld_gpio_set;
priv->chip.get_direction = mrfld_gpio_get_direction;
priv->chip.set_config = mrfld_gpio_set_config;
priv->chip.base = gpio_base;
priv->chip.ngpio = MRFLD_NGPIO;
priv->chip.can_sleep = false;
priv->chip.add_pin_ranges = mrfld_gpio_add_pin_ranges;
raw_spin_lock_init(&priv->lock); priv->info.base = gpio_base;
priv->info.ngpio = MRFLD_NGPIO;
priv->info.first = irq_base;
retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
if (retval < 0) if (retval < 0)
return retval; return retval;
girq = &priv->chip.irq; priv->irq = pci_irq_vector(pdev, 0);
gpio_irq_chip_set_chip(girq, &mrfld_irqchip);
girq->init_hw = mrfld_irq_init_hw;
girq->parent_handler = mrfld_irq_handler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents,
sizeof(*girq->parents), GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;
girq->parents[0] = pci_irq_vector(pdev, 0);
girq->first = irq_base;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); priv->wake_regs.gwmr = GWMR_MRFLD;
if (retval) { priv->wake_regs.gwsr = GWSR_MRFLD;
dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); priv->wake_regs.gsir = GSIR_MRFLD;
return retval;
} retval = devm_tng_gpio_probe(dev, priv);
if (retval)
return dev_err_probe(dev, retval, "tng_gpio_probe error\n");
pci_set_drvdata(pdev, priv); pci_set_drvdata(pdev, priv);
return 0; return 0;
...@@ -525,9 +136,9 @@ static struct pci_driver mrfld_gpio_driver = { ...@@ -525,9 +136,9 @@ static struct pci_driver mrfld_gpio_driver = {
.id_table = mrfld_gpio_ids, .id_table = mrfld_gpio_ids,
.probe = mrfld_gpio_probe, .probe = mrfld_gpio_probe,
}; };
module_pci_driver(mrfld_gpio_driver); module_pci_driver(mrfld_gpio_driver);
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
MODULE_DESCRIPTION("Intel Merrifield SoC GPIO driver"); MODULE_DESCRIPTION("Intel Merrifield SoC GPIO driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(GPIO_TANGIER);
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/gpio/legacy-of-mm-gpiochip.h.h> #include <linux/gpio/legacy-of-mm-gpiochip.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Tangier GPIO driver
*
* Copyright (c) 2016, 2021, 2023 Intel Corporation.
*
* Authors: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
* Pandith N <pandith.n@intel.com>
* Raag Jadav <raag.jadav@intel.com>
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/spinlock.h>
#include <linux/string_helpers.h>
#include <linux/types.h>
#include <linux/gpio/driver.h>
#include "gpio-tangier.h"
#define GCCR 0x000 /* Controller configuration */
#define GPLR 0x004 /* Pin level r/o */
#define GPDR 0x01c /* Pin direction */
#define GPSR 0x034 /* Pin set w/o */
#define GPCR 0x04c /* Pin clear w/o */
#define GRER 0x064 /* Rising edge detect */
#define GFER 0x07c /* Falling edge detect */
#define GFBR 0x094 /* Glitch filter bypass */
#define GIMR 0x0ac /* Interrupt mask */
#define GISR 0x0c4 /* Interrupt source */
#define GITR 0x300 /* Input type */
#define GLPR 0x318 /* Level input polarity */
/**
* struct tng_gpio_context - Context to be saved during suspend-resume
* @level: Pin level
* @gpdr: Pin direction
* @grer: Rising edge detect enable
* @gfer: Falling edge detect enable
* @gimr: Interrupt mask
* @gwmr: Wake mask
*/
struct tng_gpio_context {
u32 level;
u32 gpdr;
u32 grer;
u32 gfer;
u32 gimr;
u32 gwmr;
};
static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned int offset,
unsigned int reg)
{
struct tng_gpio *priv = gpiochip_get_data(chip);
u8 reg_offset = offset / 32;
return priv->reg_base + reg + reg_offset * 4;
}
static void __iomem *gpio_reg_and_bit(struct gpio_chip *chip, unsigned int offset,
unsigned int reg, u8 *bit)
{
struct tng_gpio *priv = gpiochip_get_data(chip);
u8 reg_offset = offset / 32;
u8 shift = offset % 32;
*bit = shift;
return priv->reg_base + reg + reg_offset * 4;
}
static int tng_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
void __iomem *gplr;
u8 shift;
gplr = gpio_reg_and_bit(chip, offset, GPLR, &shift);
return !!(readl(gplr) & BIT(shift));
}
static void tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
{
struct tng_gpio *priv = gpiochip_get_data(chip);
unsigned long flags;
void __iomem *reg;
u8 shift;
reg = gpio_reg_and_bit(chip, offset, value ? GPSR : GPCR, &shift);
raw_spin_lock_irqsave(&priv->lock, flags);
writel(BIT(shift), reg);
raw_spin_unlock_irqrestore(&priv->lock, flags);
}
static int tng_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
{
struct tng_gpio *priv = gpiochip_get_data(chip);
unsigned long flags;
void __iomem *gpdr;
u32 value;
u8 shift;
gpdr = gpio_reg_and_bit(chip, offset, GPDR, &shift);
raw_spin_lock_irqsave(&priv->lock, flags);
value = readl(gpdr);
value &= ~BIT(shift);
writel(value, gpdr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int tng_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct tng_gpio *priv = gpiochip_get_data(chip);
unsigned long flags;
void __iomem *gpdr;
u8 shift;
gpdr = gpio_reg_and_bit(chip, offset, GPDR, &shift);
tng_gpio_set(chip, offset, value);
raw_spin_lock_irqsave(&priv->lock, flags);
value = readl(gpdr);
value |= BIT(shift);
writel(value, gpdr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int tng_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
void __iomem *gpdr;
u8 shift;
gpdr = gpio_reg_and_bit(chip, offset, GPDR, &shift);
if (readl(gpdr) & BIT(shift))
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
}
static int tng_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset,
unsigned int debounce)
{
struct tng_gpio *priv = gpiochip_get_data(chip);
unsigned long flags;
void __iomem *gfbr;
u32 value;
u8 shift;
gfbr = gpio_reg_and_bit(chip, offset, GFBR, &shift);
raw_spin_lock_irqsave(&priv->lock, flags);
value = readl(gfbr);
if (debounce)
value &= ~BIT(shift);
else
value |= BIT(shift);
writel(value, gfbr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int tng_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
unsigned long config)
{
u32 debounce;
switch (pinconf_to_config_param(config)) {
case PIN_CONFIG_BIAS_DISABLE:
case PIN_CONFIG_BIAS_PULL_UP:
case PIN_CONFIG_BIAS_PULL_DOWN:
return gpiochip_generic_config(chip, offset, config);
case PIN_CONFIG_INPUT_DEBOUNCE:
debounce = pinconf_to_config_argument(config);
return tng_gpio_set_debounce(chip, offset, debounce);
default:
return -ENOTSUPP;
}
}
static void tng_irq_ack(struct irq_data *d)
{
struct tng_gpio *priv = irq_data_get_irq_chip_data(d);
irq_hw_number_t gpio = irqd_to_hwirq(d);
unsigned long flags;
void __iomem *gisr;
u8 shift;
gisr = gpio_reg_and_bit(&priv->chip, gpio, GISR, &shift);
raw_spin_lock_irqsave(&priv->lock, flags);
writel(BIT(shift), gisr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
}
static void tng_irq_unmask_mask(struct tng_gpio *priv, u32 gpio, bool unmask)
{
unsigned long flags;
void __iomem *gimr;
u32 value;
u8 shift;
gimr = gpio_reg_and_bit(&priv->chip, gpio, GIMR, &shift);
raw_spin_lock_irqsave(&priv->lock, flags);
value = readl(gimr);
if (unmask)
value |= BIT(shift);
else
value &= ~BIT(shift);
writel(value, gimr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
}
static void tng_irq_mask(struct irq_data *d)
{
struct tng_gpio *priv = irq_data_get_irq_chip_data(d);
irq_hw_number_t gpio = irqd_to_hwirq(d);
tng_irq_unmask_mask(priv, gpio, false);
gpiochip_disable_irq(&priv->chip, gpio);
}
static void tng_irq_unmask(struct irq_data *d)
{
struct tng_gpio *priv = irq_data_get_irq_chip_data(d);
irq_hw_number_t gpio = irqd_to_hwirq(d);
gpiochip_enable_irq(&priv->chip, gpio);
tng_irq_unmask_mask(priv, gpio, true);
}
static int tng_irq_set_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct tng_gpio *priv = gpiochip_get_data(gc);
irq_hw_number_t gpio = irqd_to_hwirq(d);
void __iomem *grer = gpio_reg(&priv->chip, gpio, GRER);
void __iomem *gfer = gpio_reg(&priv->chip, gpio, GFER);
void __iomem *gitr = gpio_reg(&priv->chip, gpio, GITR);
void __iomem *glpr = gpio_reg(&priv->chip, gpio, GLPR);
u8 shift = gpio % 32;
unsigned long flags;
u32 value;
raw_spin_lock_irqsave(&priv->lock, flags);
value = readl(grer);
if (type & IRQ_TYPE_EDGE_RISING)
value |= BIT(shift);
else
value &= ~BIT(shift);
writel(value, grer);
value = readl(gfer);
if (type & IRQ_TYPE_EDGE_FALLING)
value |= BIT(shift);
else
value &= ~BIT(shift);
writel(value, gfer);
/*
* To prevent glitches from triggering an unintended level interrupt,
* configure GLPR register first and then configure GITR.
*/
value = readl(glpr);
if (type & IRQ_TYPE_LEVEL_LOW)
value |= BIT(shift);
else
value &= ~BIT(shift);
writel(value, glpr);
if (type & IRQ_TYPE_LEVEL_MASK) {
value = readl(gitr);
value |= BIT(shift);
writel(value, gitr);
irq_set_handler_locked(d, handle_level_irq);
} else if (type & IRQ_TYPE_EDGE_BOTH) {
value = readl(gitr);
value &= ~BIT(shift);
writel(value, gitr);
irq_set_handler_locked(d, handle_edge_irq);
}
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int tng_irq_set_wake(struct irq_data *d, unsigned int on)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct tng_gpio *priv = gpiochip_get_data(gc);
irq_hw_number_t gpio = irqd_to_hwirq(d);
void __iomem *gwmr = gpio_reg(&priv->chip, gpio, priv->wake_regs.gwmr);
void __iomem *gwsr = gpio_reg(&priv->chip, gpio, priv->wake_regs.gwsr);
u8 shift = gpio % 32;
unsigned long flags;
u32 value;
raw_spin_lock_irqsave(&priv->lock, flags);
/* Clear the existing wake status */
writel(BIT(shift), gwsr);
value = readl(gwmr);
if (on)
value |= BIT(shift);
else
value &= ~BIT(shift);
writel(value, gwmr);
raw_spin_unlock_irqrestore(&priv->lock, flags);
dev_dbg(priv->dev, "%s wake for gpio %lu\n", str_enable_disable(on), gpio);
return 0;
}
static const struct irq_chip tng_irqchip = {
.name = "gpio-tangier",
.irq_ack = tng_irq_ack,
.irq_mask = tng_irq_mask,
.irq_unmask = tng_irq_unmask,
.irq_set_type = tng_irq_set_type,
.irq_set_wake = tng_irq_set_wake,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static void tng_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct tng_gpio *priv = gpiochip_get_data(gc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
unsigned long base, gpio;
chained_irq_enter(irqchip, desc);
/* Check GPIO controller to check which pin triggered the interrupt */
for (base = 0; base < priv->chip.ngpio; base += 32) {
void __iomem *gisr = gpio_reg(&priv->chip, base, GISR);
void __iomem *gimr = gpio_reg(&priv->chip, base, GIMR);
unsigned long pending, enabled;
pending = readl(gisr);
enabled = readl(gimr);
/* Only interrupts that are enabled */
pending &= enabled;
for_each_set_bit(gpio, &pending, 32)
generic_handle_domain_irq(gc->irq.domain, base + gpio);
}
chained_irq_exit(irqchip, desc);
}
static int tng_irq_init_hw(struct gpio_chip *chip)
{
struct tng_gpio *priv = gpiochip_get_data(chip);
void __iomem *reg;
unsigned int base;
for (base = 0; base < priv->chip.ngpio; base += 32) {
/* Clear the rising-edge detect register */
reg = gpio_reg(&priv->chip, base, GRER);
writel(0, reg);
/* Clear the falling-edge detect register */
reg = gpio_reg(&priv->chip, base, GFER);
writel(0, reg);
}
return 0;
}
static int tng_gpio_add_pin_ranges(struct gpio_chip *chip)
{
struct tng_gpio *priv = gpiochip_get_data(chip);
const struct tng_gpio_pinrange *range;
unsigned int i;
int ret;
for (i = 0; i < priv->pin_info.nranges; i++) {
range = &priv->pin_info.pin_ranges[i];
ret = gpiochip_add_pin_range(&priv->chip,
priv->pin_info.name,
range->gpio_base,
range->pin_base,
range->npins);
if (ret) {
dev_err(priv->dev, "failed to add GPIO pin range\n");
return ret;
}
}
return 0;
}
int devm_tng_gpio_probe(struct device *dev, struct tng_gpio *gpio)
{
const struct tng_gpio_info *info = &gpio->info;
struct gpio_irq_chip *girq;
int ret;
gpio->ctx = devm_kcalloc(dev, DIV_ROUND_UP(info->ngpio, 32), sizeof(*gpio->ctx), GFP_KERNEL);
if (!gpio->ctx)
return -ENOMEM;
gpio->chip.label = dev_name(dev);
gpio->chip.parent = dev;
gpio->chip.request = gpiochip_generic_request;
gpio->chip.free = gpiochip_generic_free;
gpio->chip.direction_input = tng_gpio_direction_input;
gpio->chip.direction_output = tng_gpio_direction_output;
gpio->chip.get = tng_gpio_get;
gpio->chip.set = tng_gpio_set;
gpio->chip.get_direction = tng_gpio_get_direction;
gpio->chip.set_config = tng_gpio_set_config;
gpio->chip.base = info->base;
gpio->chip.ngpio = info->ngpio;
gpio->chip.can_sleep = false;
gpio->chip.add_pin_ranges = tng_gpio_add_pin_ranges;
raw_spin_lock_init(&gpio->lock);
girq = &gpio->chip.irq;
gpio_irq_chip_set_chip(girq, &tng_irqchip);
girq->init_hw = tng_irq_init_hw;
girq->parent_handler = tng_irq_handler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(dev, girq->num_parents,
sizeof(*girq->parents), GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;
girq->parents[0] = gpio->irq;
girq->first = info->first;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
ret = devm_gpiochip_add_data(dev, &gpio->chip, gpio);
if (ret)
return dev_err_probe(dev, ret, "gpiochip_add error\n");
return 0;
}
EXPORT_SYMBOL_NS_GPL(devm_tng_gpio_probe, GPIO_TANGIER);
int tng_gpio_suspend(struct device *dev)
{
struct tng_gpio *priv = dev_get_drvdata(dev);
struct tng_gpio_context *ctx = priv->ctx;
unsigned long flags;
unsigned int base;
raw_spin_lock_irqsave(&priv->lock, flags);
for (base = 0; base < priv->chip.ngpio; base += 32, ctx++) {
/* GPLR is RO, values read will be restored using GPSR */
ctx->level = readl(gpio_reg(&priv->chip, base, GPLR));
ctx->gpdr = readl(gpio_reg(&priv->chip, base, GPDR));
ctx->grer = readl(gpio_reg(&priv->chip, base, GRER));
ctx->gfer = readl(gpio_reg(&priv->chip, base, GFER));
ctx->gimr = readl(gpio_reg(&priv->chip, base, GIMR));
ctx->gwmr = readl(gpio_reg(&priv->chip, base, priv->wake_regs.gwmr));
}
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_NS_GPL(tng_gpio_suspend, GPIO_TANGIER);
int tng_gpio_resume(struct device *dev)
{
struct tng_gpio *priv = dev_get_drvdata(dev);
struct tng_gpio_context *ctx = priv->ctx;
unsigned long flags;
unsigned int base;
raw_spin_lock_irqsave(&priv->lock, flags);
for (base = 0; base < priv->chip.ngpio; base += 32, ctx++) {
/* GPLR is RO, values read will be restored using GPSR */
writel(ctx->level, gpio_reg(&priv->chip, base, GPSR));
writel(ctx->gpdr, gpio_reg(&priv->chip, base, GPDR));
writel(ctx->grer, gpio_reg(&priv->chip, base, GRER));
writel(ctx->gfer, gpio_reg(&priv->chip, base, GFER));
writel(ctx->gimr, gpio_reg(&priv->chip, base, GIMR));
writel(ctx->gwmr, gpio_reg(&priv->chip, base, priv->wake_regs.gwmr));
}
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_NS_GPL(tng_gpio_resume, GPIO_TANGIER);
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
MODULE_AUTHOR("Pandith N <pandith.n@intel.com>");
MODULE_AUTHOR("Raag Jadav <raag.jadav@intel.com>");
MODULE_DESCRIPTION("Intel Tangier GPIO driver");
MODULE_LICENSE("GPL");
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Intel Tangier GPIO functions
*
* Copyright (c) 2016, 2021, 2023 Intel Corporation.
*
* Authors: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
* Pandith N <pandith.n@intel.com>
* Raag Jadav <raag.jadav@intel.com>
*/
#ifndef _GPIO_TANGIER_H_
#define _GPIO_TANGIER_H_
#include <linux/gpio/driver.h>
#include <linux/spinlock_types.h>
#include <linux/types.h>
struct device;
struct tng_gpio_context;
/* Elkhart Lake specific wake registers */
#define GWMR_EHL 0x100 /* Wake mask */
#define GWSR_EHL 0x118 /* Wake source */
#define GSIR_EHL 0x130 /* Secure input */
/* Merrifield specific wake registers */
#define GWMR_MRFLD 0x400 /* Wake mask */
#define GWSR_MRFLD 0x418 /* Wake source */
#define GSIR_MRFLD 0xc00 /* Secure input */
/**
* struct tng_wake_regs - Platform specific wake registers
* @gwmr: Wake mask
* @gwsr: Wake source
* @gsir: Secure input
*/
struct tng_wake_regs {
u32 gwmr;
u32 gwsr;
u32 gsir;
};
/**
* struct tng_gpio_pinrange - Map pin numbers to gpio numbers
* @gpio_base: Starting GPIO number of this range
* @pin_base: Starting pin number of this range
* @npins: Number of pins in this range
*/
struct tng_gpio_pinrange {
unsigned int gpio_base;
unsigned int pin_base;
unsigned int npins;
};
#define GPIO_PINRANGE(gstart, gend, pstart) \
(struct tng_gpio_pinrange) { \
.gpio_base = (gstart), \
.pin_base = (pstart), \
.npins = (gend) - (gstart) + 1, \
}
/**
* struct tng_gpio_pin_info - Platform specific pinout information
* @pin_ranges: Pin to GPIO mapping
* @nranges: Number of pin ranges
* @name: Respective pinctrl device name
*/
struct tng_gpio_pin_info {
const struct tng_gpio_pinrange *pin_ranges;
unsigned int nranges;
const char *name;
};
/**
* struct tng_gpio_info - Platform specific GPIO and IRQ information
* @base: GPIO base to start numbering with
* @ngpio: Amount of GPIOs supported by the controller
* @first: First IRQ to start numbering with
*/
struct tng_gpio_info {
int base;
u16 ngpio;
unsigned int first;
};
/**
* struct tng_gpio - Platform specific private data
* @chip: Instance of the struct gpio_chip
* @reg_base: Base address of MMIO registers
* @irq: Interrupt for the GPIO device
* @lock: Synchronization lock to prevent I/O race conditions
* @dev: The GPIO device
* @ctx: Context to be saved during suspend-resume
* @wake_regs: Platform specific wake registers
* @pin_info: Platform specific pinout information
* @info: Platform specific GPIO and IRQ information
*/
struct tng_gpio {
struct gpio_chip chip;
void __iomem *reg_base;
int irq;
raw_spinlock_t lock;
struct device *dev;
struct tng_gpio_context *ctx;
struct tng_wake_regs wake_regs;
struct tng_gpio_pin_info pin_info;
struct tng_gpio_info info;
};
int devm_tng_gpio_probe(struct device *dev, struct tng_gpio *gpio);
int tng_gpio_suspend(struct device *dev);
int tng_gpio_resume(struct device *dev);
#endif /* _GPIO_TANGIER_H_ */
...@@ -128,7 +128,7 @@ static bool acpi_gpio_deferred_req_irqs_done; ...@@ -128,7 +128,7 @@ static bool acpi_gpio_deferred_req_irqs_done;
static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
{ {
return gc->parent && device_match_acpi_handle(gc->parent, data); return ACPI_HANDLE_FWNODE(gc->fwnode) == data;
} }
/** /**
...@@ -645,7 +645,7 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev, ...@@ -645,7 +645,7 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev,
{ {
const struct acpi_gpio_mapping *gm; const struct acpi_gpio_mapping *gm;
if (!adev->driver_gpios) if (!adev || !adev->driver_gpios)
return false; return false;
for (gm = adev->driver_gpios; gm->name; gm++) for (gm = adev->driver_gpios; gm->name; gm++)
...@@ -839,13 +839,10 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, ...@@ -839,13 +839,10 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode,
ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, ret = __acpi_node_get_property_reference(fwnode, propname, index, 3,
&args); &args);
if (ret) { if (ret) {
struct acpi_device *adev = to_acpi_device_node(fwnode); struct acpi_device *adev;
if (!adev) adev = to_acpi_device_node(fwnode);
return ret; if (!acpi_get_driver_gpio_data(adev, propname, index, &args, &quirks))
if (!acpi_get_driver_gpio_data(adev, propname, index, &args,
&quirks))
return ret; return ret;
} }
/* /*
...@@ -1616,6 +1613,19 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { ...@@ -1616,6 +1613,19 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
.ignore_interrupt = "AMDI0030:00@18", .ignore_interrupt = "AMDI0030:00@18",
}, },
}, },
{
/*
* Spurious wakeups from TP_ATTN# pin
* Found in BIOS 1.7.8
* https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627
*/
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"),
},
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
.ignore_wake = "ELAN0415:00@9",
},
},
{ {
/* /*
* Spurious wakeups from TP_ATTN# pin * Spurious wakeups from TP_ATTN# pin
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment