162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AppliedMicro X-Gene SoC GPIO-Standby Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2014, Applied Micro Circuits Corporation
662306a36Sopenharmony_ci * Author:	Tin Huynh <tnhuynh@apm.com>.
762306a36Sopenharmony_ci *		Y Vo <yvo@apm.com>.
862306a36Sopenharmony_ci *		Quan Nguyen <qnguyen@apm.com>.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1662306a36Sopenharmony_ci#include <linux/acpi.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "gpiolib.h"
1962306a36Sopenharmony_ci#include "gpiolib-acpi.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* Common property names */
2262306a36Sopenharmony_ci#define XGENE_NIRQ_PROPERTY		"apm,nr-irqs"
2362306a36Sopenharmony_ci#define XGENE_NGPIO_PROPERTY		"apm,nr-gpios"
2462306a36Sopenharmony_ci#define XGENE_IRQ_START_PROPERTY	"apm,irq-start"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define XGENE_DFLT_MAX_NGPIO		22
2762306a36Sopenharmony_ci#define XGENE_DFLT_MAX_NIRQ		6
2862306a36Sopenharmony_ci#define XGENE_DFLT_IRQ_START_PIN	8
2962306a36Sopenharmony_ci#define GPIO_MASK(x)			(1U << ((x) % 32))
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define MPA_GPIO_INT_LVL		0x0290
3262306a36Sopenharmony_ci#define MPA_GPIO_OE_ADDR		0x029c
3362306a36Sopenharmony_ci#define MPA_GPIO_OUT_ADDR		0x02a0
3462306a36Sopenharmony_ci#define MPA_GPIO_IN_ADDR 		0x02a4
3562306a36Sopenharmony_ci#define MPA_GPIO_SEL_LO 		0x0294
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define GPIO_INT_LEVEL_H	0x000001
3862306a36Sopenharmony_ci#define GPIO_INT_LEVEL_L	0x000000
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/**
4162306a36Sopenharmony_ci * struct xgene_gpio_sb - GPIO-Standby private data structure.
4262306a36Sopenharmony_ci * @gc:				memory-mapped GPIO controllers.
4362306a36Sopenharmony_ci * @regs:			GPIO register base offset
4462306a36Sopenharmony_ci * @irq_domain:			GPIO interrupt domain
4562306a36Sopenharmony_ci * @irq_start:			GPIO pin that start support interrupt
4662306a36Sopenharmony_ci * @nirq:			Number of GPIO pins that supports interrupt
4762306a36Sopenharmony_ci * @parent_irq_base:		Start parent HWIRQ
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistruct xgene_gpio_sb {
5062306a36Sopenharmony_ci	struct gpio_chip	gc;
5162306a36Sopenharmony_ci	void __iomem		*regs;
5262306a36Sopenharmony_ci	struct irq_domain	*irq_domain;
5362306a36Sopenharmony_ci	u16			irq_start;
5462306a36Sopenharmony_ci	u16			nirq;
5562306a36Sopenharmony_ci	u16			parent_irq_base;
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define HWIRQ_TO_GPIO(priv, hwirq) ((hwirq) + (priv)->irq_start)
5962306a36Sopenharmony_ci#define GPIO_TO_HWIRQ(priv, gpio) ((gpio) - (priv)->irq_start)
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void xgene_gpio_set_bit(struct gpio_chip *gc,
6262306a36Sopenharmony_ci				void __iomem *reg, u32 gpio, int val)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	u32 data;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	data = gc->read_reg(reg);
6762306a36Sopenharmony_ci	if (val)
6862306a36Sopenharmony_ci		data |= GPIO_MASK(gpio);
6962306a36Sopenharmony_ci	else
7062306a36Sopenharmony_ci		data &= ~GPIO_MASK(gpio);
7162306a36Sopenharmony_ci	gc->write_reg(reg, data);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int xgene_gpio_sb_irq_set_type(struct irq_data *d, unsigned int type)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct xgene_gpio_sb *priv = irq_data_get_irq_chip_data(d);
7762306a36Sopenharmony_ci	int gpio = HWIRQ_TO_GPIO(priv, d->hwirq);
7862306a36Sopenharmony_ci	int lvl_type = GPIO_INT_LEVEL_H;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	switch (type & IRQ_TYPE_SENSE_MASK) {
8162306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
8262306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
8362306a36Sopenharmony_ci		lvl_type = GPIO_INT_LEVEL_H;
8462306a36Sopenharmony_ci		break;
8562306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
8662306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
8762306a36Sopenharmony_ci		lvl_type = GPIO_INT_LEVEL_L;
8862306a36Sopenharmony_ci		break;
8962306a36Sopenharmony_ci	default:
9062306a36Sopenharmony_ci		break;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,
9462306a36Sopenharmony_ci			gpio * 2, 1);
9562306a36Sopenharmony_ci	xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_INT_LVL,
9662306a36Sopenharmony_ci			d->hwirq, lvl_type);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* Propagate IRQ type setting to parent */
9962306a36Sopenharmony_ci	if (type & IRQ_TYPE_EDGE_BOTH)
10062306a36Sopenharmony_ci		return irq_chip_set_type_parent(d, IRQ_TYPE_EDGE_RISING);
10162306a36Sopenharmony_ci	else
10262306a36Sopenharmony_ci		return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic struct irq_chip xgene_gpio_sb_irq_chip = {
10662306a36Sopenharmony_ci	.name           = "sbgpio",
10762306a36Sopenharmony_ci	.irq_eoi	= irq_chip_eoi_parent,
10862306a36Sopenharmony_ci	.irq_mask       = irq_chip_mask_parent,
10962306a36Sopenharmony_ci	.irq_unmask     = irq_chip_unmask_parent,
11062306a36Sopenharmony_ci	.irq_set_type   = xgene_gpio_sb_irq_set_type,
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int xgene_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct xgene_gpio_sb *priv = gpiochip_get_data(gc);
11662306a36Sopenharmony_ci	struct irq_fwspec fwspec;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if ((gpio < priv->irq_start) ||
11962306a36Sopenharmony_ci			(gpio > HWIRQ_TO_GPIO(priv, priv->nirq)))
12062306a36Sopenharmony_ci		return -ENXIO;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	fwspec.fwnode = gc->parent->fwnode;
12362306a36Sopenharmony_ci	fwspec.param_count = 2;
12462306a36Sopenharmony_ci	fwspec.param[0] = GPIO_TO_HWIRQ(priv, gpio);
12562306a36Sopenharmony_ci	fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
12662306a36Sopenharmony_ci	return irq_create_fwspec_mapping(&fwspec);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int xgene_gpio_sb_domain_activate(struct irq_domain *d,
13062306a36Sopenharmony_ci					 struct irq_data *irq_data,
13162306a36Sopenharmony_ci					 bool reserve)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct xgene_gpio_sb *priv = d->host_data;
13462306a36Sopenharmony_ci	u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq);
13562306a36Sopenharmony_ci	int ret;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	ret = gpiochip_lock_as_irq(&priv->gc, gpio);
13862306a36Sopenharmony_ci	if (ret) {
13962306a36Sopenharmony_ci		dev_err(priv->gc.parent,
14062306a36Sopenharmony_ci		"Unable to configure XGene GPIO standby pin %d as IRQ\n",
14162306a36Sopenharmony_ci				gpio);
14262306a36Sopenharmony_ci		return ret;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,
14662306a36Sopenharmony_ci			gpio * 2, 1);
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic void xgene_gpio_sb_domain_deactivate(struct irq_domain *d,
15162306a36Sopenharmony_ci		struct irq_data *irq_data)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct xgene_gpio_sb *priv = d->host_data;
15462306a36Sopenharmony_ci	u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	gpiochip_unlock_as_irq(&priv->gc, gpio);
15762306a36Sopenharmony_ci	xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,
15862306a36Sopenharmony_ci			gpio * 2, 0);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int xgene_gpio_sb_domain_translate(struct irq_domain *d,
16262306a36Sopenharmony_ci		struct irq_fwspec *fwspec,
16362306a36Sopenharmony_ci		unsigned long *hwirq,
16462306a36Sopenharmony_ci		unsigned int *type)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct xgene_gpio_sb *priv = d->host_data;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if ((fwspec->param_count != 2) ||
16962306a36Sopenharmony_ci		(fwspec->param[0] >= priv->nirq))
17062306a36Sopenharmony_ci		return -EINVAL;
17162306a36Sopenharmony_ci	*hwirq = fwspec->param[0];
17262306a36Sopenharmony_ci	*type = fwspec->param[1];
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int xgene_gpio_sb_domain_alloc(struct irq_domain *domain,
17762306a36Sopenharmony_ci					unsigned int virq,
17862306a36Sopenharmony_ci					unsigned int nr_irqs, void *data)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct irq_fwspec *fwspec = data;
18162306a36Sopenharmony_ci	struct irq_fwspec parent_fwspec;
18262306a36Sopenharmony_ci	struct xgene_gpio_sb *priv = domain->host_data;
18362306a36Sopenharmony_ci	irq_hw_number_t hwirq;
18462306a36Sopenharmony_ci	unsigned int i;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	hwirq = fwspec->param[0];
18762306a36Sopenharmony_ci	for (i = 0; i < nr_irqs; i++)
18862306a36Sopenharmony_ci		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
18962306a36Sopenharmony_ci				&xgene_gpio_sb_irq_chip, priv);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	parent_fwspec.fwnode = domain->parent->fwnode;
19262306a36Sopenharmony_ci	if (is_of_node(parent_fwspec.fwnode)) {
19362306a36Sopenharmony_ci		parent_fwspec.param_count = 3;
19462306a36Sopenharmony_ci		parent_fwspec.param[0] = 0;/* SPI */
19562306a36Sopenharmony_ci		/* Skip SGIs and PPIs*/
19662306a36Sopenharmony_ci		parent_fwspec.param[1] = hwirq + priv->parent_irq_base - 32;
19762306a36Sopenharmony_ci		parent_fwspec.param[2] = fwspec->param[1];
19862306a36Sopenharmony_ci	} else if (is_fwnode_irqchip(parent_fwspec.fwnode)) {
19962306a36Sopenharmony_ci		parent_fwspec.param_count = 2;
20062306a36Sopenharmony_ci		parent_fwspec.param[0] = hwirq + priv->parent_irq_base;
20162306a36Sopenharmony_ci		parent_fwspec.param[1] = fwspec->param[1];
20262306a36Sopenharmony_ci	} else
20362306a36Sopenharmony_ci		return -EINVAL;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
20662306a36Sopenharmony_ci			&parent_fwspec);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic const struct irq_domain_ops xgene_gpio_sb_domain_ops = {
21062306a36Sopenharmony_ci	.translate      = xgene_gpio_sb_domain_translate,
21162306a36Sopenharmony_ci	.alloc          = xgene_gpio_sb_domain_alloc,
21262306a36Sopenharmony_ci	.free           = irq_domain_free_irqs_common,
21362306a36Sopenharmony_ci	.activate	= xgene_gpio_sb_domain_activate,
21462306a36Sopenharmony_ci	.deactivate	= xgene_gpio_sb_domain_deactivate,
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int xgene_gpio_sb_probe(struct platform_device *pdev)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct xgene_gpio_sb *priv;
22062306a36Sopenharmony_ci	int ret;
22162306a36Sopenharmony_ci	void __iomem *regs;
22262306a36Sopenharmony_ci	struct irq_domain *parent_domain = NULL;
22362306a36Sopenharmony_ci	u32 val32;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
22662306a36Sopenharmony_ci	if (!priv)
22762306a36Sopenharmony_ci		return -ENOMEM;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	regs = devm_platform_ioremap_resource(pdev, 0);
23062306a36Sopenharmony_ci	if (IS_ERR(regs))
23162306a36Sopenharmony_ci		return PTR_ERR(regs);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	priv->regs = regs;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	ret = platform_get_irq(pdev, 0);
23662306a36Sopenharmony_ci	if (ret > 0) {
23762306a36Sopenharmony_ci		priv->parent_irq_base = irq_get_irq_data(ret)->hwirq;
23862306a36Sopenharmony_ci		parent_domain = irq_get_irq_data(ret)->domain;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci	if (!parent_domain) {
24162306a36Sopenharmony_ci		dev_err(&pdev->dev, "unable to obtain parent domain\n");
24262306a36Sopenharmony_ci		return -ENODEV;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	ret = bgpio_init(&priv->gc, &pdev->dev, 4,
24662306a36Sopenharmony_ci			regs + MPA_GPIO_IN_ADDR,
24762306a36Sopenharmony_ci			regs + MPA_GPIO_OUT_ADDR, NULL,
24862306a36Sopenharmony_ci			regs + MPA_GPIO_OE_ADDR, NULL, 0);
24962306a36Sopenharmony_ci        if (ret)
25062306a36Sopenharmony_ci                return ret;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	priv->gc.to_irq = xgene_gpio_sb_to_irq;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Retrieve start irq pin, use default if property not found */
25562306a36Sopenharmony_ci	priv->irq_start = XGENE_DFLT_IRQ_START_PIN;
25662306a36Sopenharmony_ci	if (!device_property_read_u32(&pdev->dev,
25762306a36Sopenharmony_ci					XGENE_IRQ_START_PROPERTY, &val32))
25862306a36Sopenharmony_ci		priv->irq_start = val32;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Retrieve number irqs, use default if property not found */
26162306a36Sopenharmony_ci	priv->nirq = XGENE_DFLT_MAX_NIRQ;
26262306a36Sopenharmony_ci	if (!device_property_read_u32(&pdev->dev, XGENE_NIRQ_PROPERTY, &val32))
26362306a36Sopenharmony_ci		priv->nirq = val32;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* Retrieve number gpio, use default if property not found */
26662306a36Sopenharmony_ci	priv->gc.ngpio = XGENE_DFLT_MAX_NGPIO;
26762306a36Sopenharmony_ci	if (!device_property_read_u32(&pdev->dev, XGENE_NGPIO_PROPERTY, &val32))
26862306a36Sopenharmony_ci		priv->gc.ngpio = val32;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	dev_info(&pdev->dev, "Support %d gpios, %d irqs start from pin %d\n",
27162306a36Sopenharmony_ci			priv->gc.ngpio, priv->nirq, priv->irq_start);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	priv->irq_domain = irq_domain_create_hierarchy(parent_domain,
27662306a36Sopenharmony_ci					0, priv->nirq, pdev->dev.fwnode,
27762306a36Sopenharmony_ci					&xgene_gpio_sb_domain_ops, priv);
27862306a36Sopenharmony_ci	if (!priv->irq_domain)
27962306a36Sopenharmony_ci		return -ENODEV;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	priv->gc.irq.domain = priv->irq_domain;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ret = devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
28462306a36Sopenharmony_ci	if (ret) {
28562306a36Sopenharmony_ci		dev_err(&pdev->dev,
28662306a36Sopenharmony_ci			"failed to register X-Gene GPIO Standby driver\n");
28762306a36Sopenharmony_ci		irq_domain_remove(priv->irq_domain);
28862306a36Sopenharmony_ci		return ret;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Register interrupt handlers for GPIO signaled ACPI Events */
29462306a36Sopenharmony_ci	acpi_gpiochip_request_interrupts(&priv->gc);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return ret;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int xgene_gpio_sb_remove(struct platform_device *pdev)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct xgene_gpio_sb *priv = platform_get_drvdata(pdev);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	acpi_gpiochip_free_interrupts(&priv->gc);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	irq_domain_remove(priv->irq_domain);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return 0;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic const struct of_device_id xgene_gpio_sb_of_match[] = {
31162306a36Sopenharmony_ci	{.compatible = "apm,xgene-gpio-sb", },
31262306a36Sopenharmony_ci	{},
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, xgene_gpio_sb_of_match);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci#ifdef CONFIG_ACPI
31762306a36Sopenharmony_cistatic const struct acpi_device_id xgene_gpio_sb_acpi_match[] = {
31862306a36Sopenharmony_ci	{"APMC0D15", 0},
31962306a36Sopenharmony_ci	{},
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, xgene_gpio_sb_acpi_match);
32262306a36Sopenharmony_ci#endif
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic struct platform_driver xgene_gpio_sb_driver = {
32562306a36Sopenharmony_ci	.driver = {
32662306a36Sopenharmony_ci		   .name = "xgene-gpio-sb",
32762306a36Sopenharmony_ci		   .of_match_table = xgene_gpio_sb_of_match,
32862306a36Sopenharmony_ci		   .acpi_match_table = ACPI_PTR(xgene_gpio_sb_acpi_match),
32962306a36Sopenharmony_ci		   },
33062306a36Sopenharmony_ci	.probe = xgene_gpio_sb_probe,
33162306a36Sopenharmony_ci	.remove = xgene_gpio_sb_remove,
33262306a36Sopenharmony_ci};
33362306a36Sopenharmony_cimodule_platform_driver(xgene_gpio_sb_driver);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ciMODULE_AUTHOR("AppliedMicro");
33662306a36Sopenharmony_ciMODULE_DESCRIPTION("APM X-Gene GPIO Standby driver");
33762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
338