[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20230705194544.100370-3-nick.hawkins@hpe.com>
Date: Wed, 5 Jul 2023 14:45:41 -0500
From: nick.hawkins@....com
To: verdun@....com, nick.hawkins@....com, linus.walleij@...aro.org,
brgl@...ev.pl, robh+dt@...nel.org,
krzysztof.kozlowski+dt@...aro.org, jdelvare@...e.com,
linux@...ck-us.net, andy.shevchenko@...il.com,
linux-gpio@...r.kernel.org, devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org, linux-hwmon@...r.kernel.org
Subject: [PATCH v5 2/5] gpio: gxp: Add HPE GXP GPIO PL
From: Nick Hawkins <nick.hawkins@....com>
The GXP SoC supports GPIO on multiple interfaces. The interfaces are
CPLD and Host. The gpio-gxp-pl driver covers the CPLD which takes
physical I/O from the board and shares it with GXP via a proprietary
interface that maps the I/O onto a specific register area of the GXP.
This driver supports interrupts from the CPLD.
Signed-off-by: Nick Hawkins <nick.hawkins@....com>
---
v5:
*No change
v4:
*Moved gpio-gxp-pl.c to a separate commit.
*Moved regmap_config out of function and made it static const
*Removed unnecessary variables
*Removed redundant conditional
*Modified regmap_read switch statements to calculate offset and mask
then read at end.
*Removed use of -EOPNOTSUPP
*Removed redundant casting
*Switched generic_handle_irq -> generic_handle_domain_irq
*Used GENMASK where applicable
*Used bitmap_xor and for_each_bit_set
*Made GPIO chip const and marked as a template (in the name)
*Made irq_chip const and immutable
*Removed casting in one case
*Corrected check on devm_gpiochip_add_data
*Remove dev_err_probe on platform_get_irq
*Changed return 0 to devm_request_irq
v3:
*Remove shared variables with gxp-fan-ctrl
v2:
*Separated code into two files to keep size down: gpio-gxp.c and
gpio-gxp-pl.c
*Fixed Kconfig indentation as well as add new entry for gpio-gxp-pl
*Removed use of linux/of.h and linux/of_device.h
*Added mod_devicetable.h and property.h
*Fixed indentation of defines and uses consistent number of digits
*Corrected defines with improper GPIO_ namespace.
*For masks now use BIT()
*Added comment for PLREG offsets
*Move gpio_chip to be first in structure
*Calculate offset for high and low byte GPIO reads instead of having
H(High) and L(Low) letters added to the variables.
*Removed repeditive use of "? 1 : 0"
*Switched to handle_bad_irq()
*Removed improper bailout on gpiochip_add_data
*Used GENMASK to arm interrupts
*Removed use of of_match_device
*fixed sizeof in devm_kzalloc
*Added COMPILE_TEST to Kconfig
*Added dev_err_probe
*Removed unecessary parent and compatible checks
---
drivers/gpio/Kconfig | 9 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-gxp-pl.c | 582 +++++++++++++++++++++++++++++++++++++
3 files changed, 592 insertions(+)
create mode 100644 drivers/gpio/gpio-gxp-pl.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 5521f060d58e..2e0b003ccefb 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1263,6 +1263,15 @@ config HTC_EGPIO
several HTC phones. It provides basic support for input
pins, output pins, and IRQs.
+config GPIO_GXP_PL
+ tristate "GXP GPIO PL support"
+ depends on ARCH_HPE_GXP || COMPILE_TEST
+ select GPIOLIB_IRQCHIP
+ help
+ Say Y here to support GXP GPIO PL controller. It provides
+ support for the GPIO PL interface available to be
+ available to the Host.
+
config GPIO_ELKHARTLAKE
tristate "Intel Elkhart Lake PSE GPIO support"
depends on X86 || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 20036af3acb1..8903f446ef1b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_GPIO_FXL6408) += gpio-fxl6408.o
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o
obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
+obj-$(CONFIG_GPIO_GXP_PL) += gpio-gxp-pl.o
obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o
obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o
obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o
diff --git a/drivers/gpio/gpio-gxp-pl.c b/drivers/gpio/gpio-gxp-pl.c
new file mode 100644
index 000000000000..8506e2a96da4
--- /dev/null
+++ b/drivers/gpio/gpio-gxp-pl.c
@@ -0,0 +1,582 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2023 Hewlett-Packard Enterprise Development Company, L.P. */
+
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+/* Specific offsets in CPLD registers for interrupts */
+#define PLREG_INT_GRP_STAT_MASK 0x08
+#define PLREG_INT_HI_PRI_EN 0x0C
+#define PLREG_INT_GRP5_BASE 0x31
+#define PLREG_INT_GRP6_BASE 0x35
+#define PLREG_INT_GRP5_FLAG 0x30
+#define PLREG_INT_GRP5_STATE 0x32
+#define PLREG_INT_GRP6_FLAG 0x34
+
+/* Specific bits to enable Group 4 and Group 5 interrupts */
+#define PLREG_GRP4_GRP5_MASK GENMASK(5, 4)
+
+/* Specific offsets in CPLD registers */
+#define PLREG_SERVER_ID 0x01 /* 2 Bytes */
+#define PLREG_IOP_LED 0x04
+#define PLREG_IDENT_LED 0x05
+#define PLREG_HEALTH_LED 0x0D
+#define PLREG_PSU_INST 0x19
+#define PLREG_PSU_AC 0x1B
+#define PLREG_PSU_DC 0x1C
+#define PLREG_FAN_INST 0x27
+#define PLREG_FAN_FAIL 0x29
+#define PLREG_SIDEBAND 0x40
+#define GXP_GPIO_DIR_OUT 0x00
+#define GXP_GPIO_DIR_IN 0x01
+
+enum pl_gpio_pn {
+ IOP_LED1 = 0,
+ IOP_LED2 = 1,
+ IOP_LED3 = 2,
+ IOP_LED4 = 3,
+ IOP_LED5 = 4,
+ IOP_LED6 = 5,
+ IOP_LED7 = 6,
+ IOP_LED8 = 7,
+ FAN1_INST = 8,
+ FAN2_INST = 9,
+ FAN3_INST = 10,
+ FAN4_INST = 11,
+ FAN5_INST = 12,
+ FAN6_INST = 13,
+ FAN7_INST = 14,
+ FAN8_INST = 15,
+ FAN1_FAIL = 16,
+ FAN2_FAIL = 17,
+ FAN3_FAIL = 18,
+ FAN4_FAIL = 19,
+ FAN5_FAIL = 20,
+ FAN6_FAIL = 21,
+ FAN7_FAIL = 22,
+ FAN8_FAIL = 23,
+ LED_IDENTIFY = 24,
+ LED_HEALTH_RED = 25,
+ LED_HEALTH_AMBER = 26,
+ PWR_BTN_INT = 27,
+ UID_PRESS_INT = 28,
+ SLP_INT = 29,
+ ACM_FORCE_OFF = 30,
+ ACM_REMOVED = 31,
+ ACM_REQ_N = 32,
+ PSU1_INST = 33,
+ PSU2_INST = 34,
+ PSU3_INST = 35,
+ PSU4_INST = 36,
+ PSU5_INST = 37,
+ PSU6_INST = 38,
+ PSU7_INST = 39,
+ PSU8_INST = 40,
+ PSU1_AC = 41,
+ PSU2_AC = 42,
+ PSU3_AC = 43,
+ PSU4_AC = 44,
+ PSU5_AC = 45,
+ PSU6_AC = 46,
+ PSU7_AC = 47,
+ PSU8_AC = 48,
+ PSU1_DC = 49,
+ PSU2_DC = 50,
+ PSU3_DC = 51,
+ PSU4_DC = 52,
+ PSU5_DC = 53,
+ PSU6_DC = 54,
+ PSU7_DC = 55,
+ PSU8_DC = 56,
+ RESET = 57,
+ NMI_OUT = 58,
+ VPBTN = 59,
+ PGOOD = 60,
+ PERST = 61,
+ POST_COMPLETE = 62,
+ SIDEBAND_SEL_L = 63,
+ SIDEBAND_SEL_H = 64
+};
+
+/*
+ * When an interrupt fires for a PSU config change
+ * there is a need to know the previous PSU configuration
+ * so that the appropriate gpio line is interrupted for
+ * the correct PSU. In order to keep this variable up to
+ * date it is global so that it can be set at init and
+ * each time the interrupt fires.
+ */
+u8 psu_presence;
+
+struct gxp_gpio_drvdata {
+ struct gpio_chip chip;
+ struct regmap *base;
+ struct regmap *interrupt;
+ int irq;
+};
+
+static const struct regmap_config gxp_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x80,
+ .name = "gxp-gpio-pl",
+};
+
+static const struct regmap_config gxp_int_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x7f,
+ .name = "gxp-gpio-pl-int",
+};
+
+static struct regmap *gxp_gpio_init_regmap(struct platform_device *pdev,
+ char *reg_name, bool is_interrupt)
+{
+ void __iomem *base;
+
+ base = devm_platform_ioremap_resource_byname(pdev, reg_name);
+ if (IS_ERR(base))
+ return ERR_CAST(base);
+
+ if (is_interrupt)
+ return devm_regmap_init_mmio(&pdev->dev, base, &gxp_int_regmap_config);
+ else
+ return devm_regmap_init_mmio(&pdev->dev, base, &gxp_regmap_config);
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+
+static int gxp_gpio_serverid_show(struct seq_file *s, void *unused)
+{
+ struct gxp_gpio_drvdata *drvdata = dev_get_drvdata(s->private);
+ unsigned int server_id_l;
+ unsigned int server_id_h;
+
+ regmap_read(drvdata->base, PLREG_SERVER_ID, &server_id_l);
+ regmap_read(drvdata->base, PLREG_SERVER_ID + 0x01, &server_id_h);
+
+ seq_printf(s, "%02x %02x", server_id_h, server_id_l);
+
+ return 0;
+}
+
+static void gxp_gpio_debuginit(struct platform_device *pdev)
+{
+ debugfs_create_devm_seqfile(&pdev->dev, "gxp_gpio_serverid", NULL,
+ gxp_gpio_serverid_show);
+}
+
+#else
+
+static inline void gxp_gpio_debuginit(struct platform_device *pdev)
+{
+}
+
+#endif
+
+static int gxp_gpio_pl_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gxp_gpio_drvdata *drvdata = dev_get_drvdata(chip->parent);
+ unsigned int val;
+ unsigned int reg_offset;
+ u8 reg_mask;
+ bool is_active_low = false;
+
+ switch (offset) {
+ case IOP_LED1 ... IOP_LED8:
+ reg_offset = PLREG_IOP_LED;
+ reg_mask = BIT(offset);
+ break;
+ case FAN1_INST ...FAN8_INST:
+ regmap_read(drvdata->base, PLREG_FAN_INST, &val);
+ reg_mask = BIT(offset - FAN1_INST);
+ break;
+ case FAN1_FAIL ... FAN8_FAIL:
+ regmap_read(drvdata->base, PLREG_FAN_FAIL, &val);
+ reg_mask = BIT(offset - FAN1_FAIL);
+ break;
+ case PWR_BTN_INT ... SLP_INT:
+ /* Note this is active low */
+ reg_offset = PLREG_INT_GRP5_STATE;
+ reg_mask = BIT(offset - PWR_BTN_INT);
+ is_active_low = true;
+ break;
+ case PSU1_INST ... PSU8_INST:
+ reg_offset = PLREG_PSU_INST;
+ reg_mask = BIT(offset - PSU1_INST);
+ break;
+ case PSU1_AC ... PSU8_AC:
+ reg_offset = PLREG_PSU_AC;
+ reg_mask = BIT(offset - PSU1_AC);
+ break;
+ case PSU1_DC ... PSU8_DC:
+ reg_offset = PLREG_PSU_DC;
+ reg_mask = BIT(offset - PSU1_DC);
+ break;
+ case LED_IDENTIFY:
+ reg_offset = PLREG_IDENT_LED;
+ reg_mask = BIT(1);
+ break;
+ case LED_HEALTH_RED:
+ reg_offset = PLREG_HEALTH_LED;
+ reg_mask = GENMASK(5, 4); /* Bit 5 set, bit 4 clear */
+ break;
+ case LED_HEALTH_AMBER:
+ reg_offset = PLREG_HEALTH_LED;
+ reg_mask = GENMASK(5, 4); /* Bit 5, bit 4 set */
+ break;
+ case SIDEBAND_SEL_L:
+ reg_offset = PLREG_SIDEBAND;
+ reg_mask = BIT(0);
+ break;
+ case SIDEBAND_SEL_H:
+ reg_offset = PLREG_SIDEBAND;
+ reg_mask = BIT(1);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ regmap_read(drvdata->base, reg_offset, &val);
+
+ /* Special case: Check two bits for Health LED */
+ if (offset == LED_HEALTH_RED)
+ /* Bit 5 set, bit 4 not set */
+ return ((val & reg_mask) == BIT(5) ? 1 : 0);
+ else if (offset == LED_HEALTH_AMBER)
+ /* Bit 5 and 4 set */
+ return ((val & reg_mask) == reg_mask ? 1 : 0);
+
+ val = val & reg_mask;
+
+ if (is_active_low)
+ return (val ? 0 : 1);
+ else
+ return (val ? 1 : 0);
+}
+
+static void gxp_gpio_pl_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct gxp_gpio_drvdata *drvdata = dev_get_drvdata(chip->parent);
+
+ switch (offset) {
+ case IOP_LED1 ... IOP_LED8:
+ regmap_update_bits(drvdata->base,
+ PLREG_IOP_LED,
+ BIT(offset),
+ value == 0 ? 0 : BIT(offset));
+ break;
+ case LED_IDENTIFY:
+ regmap_update_bits(drvdata->base,
+ PLREG_IDENT_LED,
+ GENMASK(7, 6),
+ value == 0 ? BIT(7) : GENMASK(7, 6));
+ break;
+ case LED_HEALTH_RED:
+ regmap_update_bits(drvdata->base,
+ PLREG_HEALTH_LED,
+ GENMASK(7, 6),
+ value == 0 ? 0 : BIT(7));
+ break;
+ case LED_HEALTH_AMBER:
+ regmap_update_bits(drvdata->base,
+ PLREG_HEALTH_LED,
+ GENMASK(7, 6),
+ value == 0 ? 0 : BIT(6));
+ break;
+ case SIDEBAND_SEL_L ... SIDEBAND_SEL_H:
+ regmap_update_bits(drvdata->base,
+ PLREG_SIDEBAND,
+ BIT(offset - SIDEBAND_SEL_L),
+ value == 0 ? 0 : BIT(offset - SIDEBAND_SEL_L));
+ break;
+ default:
+ break;
+ }
+}
+
+static int gxp_gpio_pl_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+ switch (offset) {
+ case IOP_LED1 ... IOP_LED8:
+ case LED_IDENTIFY ... LED_HEALTH_AMBER:
+ case ACM_FORCE_OFF:
+ case ACM_REQ_N:
+ case SIDEBAND_SEL_L ... SIDEBAND_SEL_H:
+ return GXP_GPIO_DIR_OUT;
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int gxp_gpio_pl_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ switch (offset) {
+ case FAN1_INST ... FAN8_FAIL:
+ return GXP_GPIO_DIR_OUT;
+ case PWR_BTN_INT ... SLP_INT:
+ return GXP_GPIO_DIR_OUT;
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int gxp_gpio_pl_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ switch (offset) {
+ case IOP_LED1 ... IOP_LED8:
+ case LED_IDENTIFY ... LED_HEALTH_AMBER:
+ case ACM_FORCE_OFF:
+ case ACM_REQ_N:
+ case SIDEBAND_SEL_L ... SIDEBAND_SEL_H:
+ gxp_gpio_pl_set(chip, offset, value);
+ return GXP_GPIO_DIR_OUT;
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static void gxp_gpio_pl_irq_ack(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct gxp_gpio_drvdata *drvdata = dev_get_drvdata(chip->parent);
+ unsigned int val;
+
+ /* Read latched interrupt for group 5 */
+ regmap_read(drvdata->interrupt, PLREG_INT_GRP5_FLAG, &val);
+ /* Clear latched interrupt */
+ regmap_update_bits(drvdata->interrupt, PLREG_INT_GRP5_FLAG,
+ 0xFF, 0xFF);
+
+ /* Read latched interrupt for group 6 */
+ regmap_read(drvdata->interrupt, PLREG_INT_GRP6_FLAG, &val);
+ /* Clear latched interrupt */
+ regmap_update_bits(drvdata->interrupt, PLREG_INT_GRP6_FLAG,
+ 0xFF, 0xFF);
+}
+
+static void gxp_gpio_pl_irq_set_mask(struct irq_data *d, bool set)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct gxp_gpio_drvdata *drvdata = dev_get_drvdata(chip->parent);
+
+ regmap_update_bits(drvdata->interrupt, PLREG_INT_GRP5_BASE,
+ BIT(0) | BIT(2), set ? 0 : BIT(0) | BIT(2));
+
+ regmap_update_bits(drvdata->interrupt, PLREG_INT_GRP6_BASE,
+ BIT(2), set ? 0 : BIT(2));
+}
+
+static void gxp_gpio_pl_irq_mask(struct irq_data *d)
+{
+ gxp_gpio_pl_irq_set_mask(d, false);
+}
+
+static void gxp_gpio_pl_irq_unmask(struct irq_data *d)
+{
+ gxp_gpio_pl_irq_set_mask(d, true);
+}
+
+static int gxp_gpio_irq_init_hw(struct gpio_chip *chip)
+{
+ struct gxp_gpio_drvdata *drvdata = dev_get_drvdata(chip->parent);
+
+ regmap_update_bits(drvdata->interrupt, PLREG_INT_GRP5_BASE,
+ BIT(0) | BIT(2), 0);
+
+ regmap_update_bits(drvdata->interrupt, PLREG_INT_GRP6_BASE,
+ BIT(2), 0);
+
+ return 0;
+}
+
+static int gxp_gpio_pl_set_type(struct irq_data *d, unsigned int type)
+{
+ if (type & IRQ_TYPE_LEVEL_MASK)
+ irq_set_handler_locked(d, handle_level_irq);
+ else
+ irq_set_handler_locked(d, handle_edge_irq);
+
+ return 0;
+}
+
+static irqreturn_t gxp_gpio_pl_irq_handle(int irq, void *_drvdata)
+{
+ struct gxp_gpio_drvdata *drvdata = _drvdata;
+ unsigned int val, i;
+ unsigned long temp;
+
+ /* Check group 5 interrupts */
+
+ regmap_read(drvdata->base, PLREG_INT_GRP5_FLAG, &val);
+
+ temp = (unsigned long)val;
+ for_each_set_bit(i, &temp, 3) {
+ generic_handle_domain_irq(drvdata->chip.irq.domain,
+ i + PWR_BTN_INT);
+ }
+
+ /* Clear latched interrupt */
+ regmap_update_bits(drvdata->interrupt, PLREG_INT_GRP5_FLAG,
+ GENMASK(7, 0), GENMASK(7, 0));
+ regmap_update_bits(drvdata->interrupt, PLREG_INT_GRP5_BASE,
+ BIT(0) | BIT(2), 0);
+
+ /* Check group 6 interrupts */
+
+ regmap_read(drvdata->base, PLREG_INT_GRP6_FLAG, &val);
+
+ if (val & BIT(2)) {
+ u8 old_psu = psu_presence;
+
+ regmap_read(drvdata->base, PLREG_PSU_INST, &val);
+ psu_presence = val;
+
+ if (old_psu != psu_presence) {
+ /* Identify all bits which differs */
+ unsigned long current_val = psu_presence;
+ unsigned long old_val = old_psu;
+ unsigned long changed_bits;
+
+ bitmap_xor(&changed_bits, ¤t_val, &old_val, 8);
+
+ for_each_set_bit(i, &changed_bits, 8) {
+ /* PSU state has changed */
+ generic_handle_domain_irq(drvdata->chip.irq.domain,
+ i + PSU1_INST);
+ }
+ }
+ }
+
+ /* Clear latched interrupt */
+ regmap_update_bits(drvdata->interrupt, PLREG_INT_GRP6_FLAG,
+ GENMASK(7, 0), GENMASK(7, 0));
+ regmap_update_bits(drvdata->interrupt, PLREG_INT_GRP6_BASE,
+ BIT(2), 0);
+
+ return IRQ_HANDLED;
+}
+
+static const struct gpio_chip template_chip = {
+ .label = "gxp_gpio_plreg",
+ .owner = THIS_MODULE,
+ .get = gxp_gpio_pl_get,
+ .set = gxp_gpio_pl_set,
+ .get_direction = gxp_gpio_pl_get_direction,
+ .direction_input = gxp_gpio_pl_direction_input,
+ .direction_output = gxp_gpio_pl_direction_output,
+ .base = -1,
+};
+
+static const struct irq_chip gxp_plreg_irqchip = {
+ .name = "gxp_plreg",
+ .irq_ack = gxp_gpio_pl_irq_ack,
+ .irq_mask = gxp_gpio_pl_irq_mask,
+ .irq_unmask = gxp_gpio_pl_irq_unmask,
+ .irq_set_type = gxp_gpio_pl_set_type,
+ .flags = IRQCHIP_IMMUTABLE,
+};
+
+static const struct of_device_id gxp_gpio_of_match[] = {
+ { .compatible = "hpe,gxp-gpio-pl" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, gxp_gpio_of_match);
+
+static int gxp_gpio_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct gxp_gpio_drvdata *drvdata;
+ struct gpio_irq_chip *girq;
+ unsigned int val;
+
+ /* Initialize global vars */
+ psu_presence = 0;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, drvdata);
+
+ drvdata->base = gxp_gpio_init_regmap(pdev, "base", false);
+ if (IS_ERR(drvdata->base))
+ return dev_err_probe(&pdev->dev, PTR_ERR(drvdata->base),
+ "failed to map base\n");
+
+ drvdata->interrupt = gxp_gpio_init_regmap(pdev, "interrupt", true);
+ if (IS_ERR(drvdata->interrupt))
+ return dev_err_probe(&pdev->dev, PTR_ERR(drvdata->interrupt),
+ "failed to map interrupt base\n");
+
+ /* Necessary to read the server id */
+ gxp_gpio_debuginit(pdev);
+
+ /* Initialize psu_presence variable */
+ regmap_read(drvdata->base, PLREG_PSU_INST, &val);
+ psu_presence = val;
+
+ drvdata->chip = template_chip;
+ drvdata->chip.ngpio = 80;
+ drvdata->chip.parent = &pdev->dev;
+
+ girq = &drvdata->chip.irq;
+ gpio_irq_chip_set_chip(girq, &gxp_plreg_irqchip);
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_bad_irq;
+
+ girq->init_hw = gxp_gpio_irq_init_hw;
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &drvdata->chip, drvdata);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "Could not register gpiochip for plreg\n");
+
+ regmap_update_bits(drvdata->interrupt,
+ PLREG_INT_HI_PRI_EN,
+ PLREG_GRP4_GRP5_MASK,
+ PLREG_GRP4_GRP5_MASK);
+ regmap_update_bits(drvdata->interrupt,
+ PLREG_INT_GRP_STAT_MASK,
+ PLREG_GRP4_GRP5_MASK,
+ 0x00);
+
+ regmap_read(drvdata->interrupt, PLREG_INT_HI_PRI_EN, &val);
+ regmap_read(drvdata->interrupt, PLREG_INT_GRP_STAT_MASK, &val);
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+
+ drvdata->irq = ret;
+
+ return devm_request_irq(&pdev->dev, drvdata->irq, gxp_gpio_pl_irq_handle,
+ IRQF_SHARED, "gxp-pl", drvdata);
+}
+
+static struct platform_driver gxp_gpio_driver = {
+ .driver = {
+ .name = "gxp-gpio-pl",
+ .of_match_table = gxp_gpio_of_match,
+ },
+ .probe = gxp_gpio_probe,
+};
+module_platform_driver(gxp_gpio_driver);
+
+MODULE_AUTHOR("Nick Hawkins <nick.hawkins@....com>");
+MODULE_DESCRIPTION("GPIO PL interface for GXP");
+MODULE_LICENSE("GPL");
--
2.17.1
Powered by blists - more mailing lists