summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/boot/dts/qcom-apq8060-dragonboard.dts21
-rw-r--r--arch/arm/boot/dts/qcom-apq8064.dtsi46
-rw-r--r--arch/arm/boot/dts/qcom-mdm9615.dtsi9
-rw-r--r--arch/arm/boot/dts/qcom-msm8660.dtsi47
-rw-r--r--drivers/mfd/Kconfig2
-rw-r--r--drivers/mfd/qcom-pm8xxx.c75
-rw-r--r--drivers/pinctrl/qcom/Kconfig1
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c157
-rw-r--r--include/linux/irq.h1
-rw-r--r--include/linux/irqdomain.h5
-rw-r--r--kernel/irq/chip.c11
-rw-r--r--kernel/irq/irqdomain.c45
12 files changed, 236 insertions, 184 deletions
diff --git a/arch/arm/boot/dts/qcom-apq8060-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8060-dragonboard.dts
index 497bb065eb9d..4e6c50d45cb2 100644
--- a/arch/arm/boot/dts/qcom-apq8060-dragonboard.dts
+++ b/arch/arm/boot/dts/qcom-apq8060-dragonboard.dts
@@ -93,9 +93,8 @@
vdd-supply = <&pm8058_l14>; // 2.85V
aset-gpios = <&pm8058_gpio 35 GPIO_ACTIVE_LOW>;
capella,aset-resistance-ohms = <100000>;
- /* GPIO34 has interrupt 225 on the PM8058 */
/* Trig on both edges - getting close or far away */
- interrupts-extended = <&pm8058 225 IRQ_TYPE_EDGE_BOTH>;
+ interrupts-extended = <&pm8058_gpio 34 IRQ_TYPE_EDGE_BOTH>;
/* MPP05 analog input to the XOADC */
io-channels = <&xoadc 0x00 0x05>;
io-channel-names = "aout";
@@ -515,9 +514,8 @@
ak8975@c {
compatible = "asahi-kasei,ak8975";
reg = <0x0c>;
- /* FIXME: GPIO33 has interrupt 224 on the PM8058 */
- interrupt-parent = <&pm8058>;
- interrupts = <224 IRQ_TYPE_EDGE_RISING>;
+ interrupt-parent = <&pm8058_gpio>;
+ interrupts = <33 IRQ_TYPE_EDGE_RISING>;
pinctrl-names = "default";
pinctrl-0 = <&dragon_ak8975_gpios>;
vid-supply = <&pm8058_lvs0>; // 1.8V
@@ -526,9 +524,8 @@
bmp085@77 {
compatible = "bosch,bmp085";
reg = <0x77>;
- /* FIXME: GPIO16 has interrupt 207 on the PM8058 */
- interrupt-parent = <&pm8058>;
- interrupts = <207 IRQ_TYPE_EDGE_RISING>;
+ interrupt-parent = <&pm8058_gpio>;
+ interrupts = <16 IRQ_TYPE_EDGE_RISING>;
reset-gpios = <&tlmm 86 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&dragon_bmp085_gpios>;
@@ -539,12 +536,11 @@
compatible = "invensense,mpu3050";
reg = <0x68>;
/*
- * GPIO17 has interrupt 208 on the
- * PM8058, it is pulled high by a 10k
+ * GPIO17 is pulled high by a 10k
* resistor to VLOGIC so needs to be
* active low/falling edge.
*/
- interrupts-extended = <&pm8058 208 IRQ_TYPE_EDGE_FALLING>;
+ interrupts-extended = <&pm8058_gpio 17 IRQ_TYPE_EDGE_FALLING>;
pinctrl-names = "default";
pinctrl-0 = <&dragon_mpu3050_gpios>;
vlogic-supply = <&pm8058_lvs0>; // 1.8V
@@ -589,11 +585,10 @@
compatible = "smsc,lan9221", "smsc,lan9115";
reg = <2 0x0 0x100>;
/*
- * GPIO7 has interrupt 198 on the PM8058
* The second interrupt is the PME interrupt
* for network wakeup, connected to the TLMM.
*/
- interrupts-extended = <&pm8058 198 IRQ_TYPE_EDGE_FALLING>,
+ interrupts-extended = <&pm8058_gpio 7 IRQ_TYPE_EDGE_FALLING>,
<&tlmm 29 IRQ_TYPE_EDGE_RISING>;
reset-gpios = <&tlmm 30 GPIO_ACTIVE_LOW>;
vdd33a-supply = <&dragon_veth>;
diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
index 48c3cf427610..4744fe757cf4 100644
--- a/arch/arm/boot/dts/qcom-apq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
@@ -705,50 +705,8 @@
compatible = "qcom,pm8921-gpio",
"qcom,ssbi-gpio";
reg = <0x150>;
- interrupts = <192 IRQ_TYPE_NONE>,
- <193 IRQ_TYPE_NONE>,
- <194 IRQ_TYPE_NONE>,
- <195 IRQ_TYPE_NONE>,
- <196 IRQ_TYPE_NONE>,
- <197 IRQ_TYPE_NONE>,
- <198 IRQ_TYPE_NONE>,
- <199 IRQ_TYPE_NONE>,
- <200 IRQ_TYPE_NONE>,
- <201 IRQ_TYPE_NONE>,
- <202 IRQ_TYPE_NONE>,
- <203 IRQ_TYPE_NONE>,
- <204 IRQ_TYPE_NONE>,
- <205 IRQ_TYPE_NONE>,
- <206 IRQ_TYPE_NONE>,
- <207 IRQ_TYPE_NONE>,
- <208 IRQ_TYPE_NONE>,
- <209 IRQ_TYPE_NONE>,
- <210 IRQ_TYPE_NONE>,
- <211 IRQ_TYPE_NONE>,
- <212 IRQ_TYPE_NONE>,
- <213 IRQ_TYPE_NONE>,
- <214 IRQ_TYPE_NONE>,
- <215 IRQ_TYPE_NONE>,
- <216 IRQ_TYPE_NONE>,
- <217 IRQ_TYPE_NONE>,
- <218 IRQ_TYPE_NONE>,
- <219 IRQ_TYPE_NONE>,
- <220 IRQ_TYPE_NONE>,
- <221 IRQ_TYPE_NONE>,
- <222 IRQ_TYPE_NONE>,
- <223 IRQ_TYPE_NONE>,
- <224 IRQ_TYPE_NONE>,
- <225 IRQ_TYPE_NONE>,
- <226 IRQ_TYPE_NONE>,
- <227 IRQ_TYPE_NONE>,
- <228 IRQ_TYPE_NONE>,
- <229 IRQ_TYPE_NONE>,
- <230 IRQ_TYPE_NONE>,
- <231 IRQ_TYPE_NONE>,
- <232 IRQ_TYPE_NONE>,
- <233 IRQ_TYPE_NONE>,
- <234 IRQ_TYPE_NONE>,
- <235 IRQ_TYPE_NONE>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
gpio-controller;
#gpio-cells = <2>;
diff --git a/arch/arm/boot/dts/qcom-mdm9615.dtsi b/arch/arm/boot/dts/qcom-mdm9615.dtsi
index c852b69229c9..0ed6fc3e873c 100644
--- a/arch/arm/boot/dts/qcom-mdm9615.dtsi
+++ b/arch/arm/boot/dts/qcom-mdm9615.dtsi
@@ -323,13 +323,8 @@
pmicgpio: gpio@150 {
compatible = "qcom,pm8018-gpio", "qcom,ssbi-gpio";
- interrupt-parent = <&pmicintc>;
- interrupts = <24 IRQ_TYPE_NONE>,
- <25 IRQ_TYPE_NONE>,
- <26 IRQ_TYPE_NONE>,
- <27 IRQ_TYPE_NONE>,
- <28 IRQ_TYPE_NONE>,
- <29 IRQ_TYPE_NONE>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
gpio-controller;
#gpio-cells = <2>;
};
diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi
index 70698941f64c..a4eeebcfd63a 100644
--- a/arch/arm/boot/dts/qcom-msm8660.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8660.dtsi
@@ -285,51 +285,8 @@
compatible = "qcom,pm8058-gpio",
"qcom,ssbi-gpio";
reg = <0x150>;
- interrupt-parent = <&pm8058>;
- interrupts = <192 IRQ_TYPE_NONE>,
- <193 IRQ_TYPE_NONE>,
- <194 IRQ_TYPE_NONE>,
- <195 IRQ_TYPE_NONE>,
- <196 IRQ_TYPE_NONE>,
- <197 IRQ_TYPE_NONE>,
- <198 IRQ_TYPE_NONE>,
- <199 IRQ_TYPE_NONE>,
- <200 IRQ_TYPE_NONE>,
- <201 IRQ_TYPE_NONE>,
- <202 IRQ_TYPE_NONE>,
- <203 IRQ_TYPE_NONE>,
- <204 IRQ_TYPE_NONE>,
- <205 IRQ_TYPE_NONE>,
- <206 IRQ_TYPE_NONE>,
- <207 IRQ_TYPE_NONE>,
- <208 IRQ_TYPE_NONE>,
- <209 IRQ_TYPE_NONE>,
- <210 IRQ_TYPE_NONE>,
- <211 IRQ_TYPE_NONE>,
- <212 IRQ_TYPE_NONE>,
- <213 IRQ_TYPE_NONE>,
- <214 IRQ_TYPE_NONE>,
- <215 IRQ_TYPE_NONE>,
- <216 IRQ_TYPE_NONE>,
- <217 IRQ_TYPE_NONE>,
- <218 IRQ_TYPE_NONE>,
- <219 IRQ_TYPE_NONE>,
- <220 IRQ_TYPE_NONE>,
- <221 IRQ_TYPE_NONE>,
- <222 IRQ_TYPE_NONE>,
- <223 IRQ_TYPE_NONE>,
- <224 IRQ_TYPE_NONE>,
- <225 IRQ_TYPE_NONE>,
- <226 IRQ_TYPE_NONE>,
- <227 IRQ_TYPE_NONE>,
- <228 IRQ_TYPE_NONE>,
- <229 IRQ_TYPE_NONE>,
- <230 IRQ_TYPE_NONE>,
- <231 IRQ_TYPE_NONE>,
- <232 IRQ_TYPE_NONE>,
- <233 IRQ_TYPE_NONE>,
- <234 IRQ_TYPE_NONE>,
- <235 IRQ_TYPE_NONE>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
gpio-controller;
#gpio-cells = <2>;
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 76f9909cf396..5f1349c86a6f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -928,7 +928,7 @@ config UCB1400_CORE
config MFD_PM8XXX
tristate "Qualcomm PM8xxx PMIC chips driver"
depends on (ARM || HEXAGON || COMPILE_TEST)
- select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY
select MFD_CORE
select REGMAP
help
diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c
index e6e8d81c15fd..8eb2528793f9 100644
--- a/drivers/mfd/qcom-pm8xxx.c
+++ b/drivers/mfd/qcom-pm8xxx.c
@@ -70,22 +70,23 @@
#define PM8XXX_NR_IRQS 256
#define PM8821_NR_IRQS 112
+struct pm_irq_data {
+ int num_irqs;
+ struct irq_chip *irq_chip;
+ void (*irq_handler)(struct irq_desc *desc);
+};
+
struct pm_irq_chip {
struct regmap *regmap;
spinlock_t pm_irq_lock;
struct irq_domain *irqdomain;
- unsigned int num_irqs;
unsigned int num_blocks;
unsigned int num_masters;
+ const struct pm_irq_data *pm_irq_data;
+ /* MUST BE AT THE END OF THIS STRUCT */
u8 config[0];
};
-struct pm_irq_data {
- int num_irqs;
- const struct irq_domain_ops *irq_domain_ops;
- void (*irq_handler)(struct irq_desc *desc);
-};
-
static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
unsigned int *ip)
{
@@ -375,21 +376,38 @@ static struct irq_chip pm8xxx_irq_chip = {
.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
};
-static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hwirq)
+static void pm8xxx_irq_domain_map(struct pm_irq_chip *chip,
+ struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq, unsigned int type)
{
- struct pm_irq_chip *chip = d->host_data;
-
- irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq);
- irq_set_chip_data(irq, chip);
+ irq_domain_set_info(domain, irq, hwirq, chip->pm_irq_data->irq_chip,
+ chip, handle_level_irq, NULL, NULL);
irq_set_noprobe(irq);
+}
+
+static int pm8xxx_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct pm_irq_chip *chip = domain->host_data;
+ struct irq_fwspec *fwspec = data;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ int ret, i;
+
+ ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++)
+ pm8xxx_irq_domain_map(chip, domain, virq + i, hwirq + i, type);
return 0;
}
static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
- .xlate = irq_domain_xlate_twocell,
- .map = pm8xxx_irq_domain_map,
+ .alloc = pm8xxx_irq_domain_alloc,
+ .free = irq_domain_free_irqs_common,
+ .translate = irq_domain_translate_twocell,
};
static void pm8821_irq_mask_ack(struct irq_data *d)
@@ -473,23 +491,6 @@ static struct irq_chip pm8821_irq_chip = {
.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
};
-static int pm8821_irq_domain_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hwirq)
-{
- struct pm_irq_chip *chip = d->host_data;
-
- irq_set_chip_and_handler(irq, &pm8821_irq_chip, handle_level_irq);
- irq_set_chip_data(irq, chip);
- irq_set_noprobe(irq);
-
- return 0;
-}
-
-static const struct irq_domain_ops pm8821_irq_domain_ops = {
- .xlate = irq_domain_xlate_twocell,
- .map = pm8821_irq_domain_map,
-};
-
static const struct regmap_config ssbi_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
@@ -501,13 +502,13 @@ static const struct regmap_config ssbi_regmap_config = {
static const struct pm_irq_data pm8xxx_data = {
.num_irqs = PM8XXX_NR_IRQS,
- .irq_domain_ops = &pm8xxx_irq_domain_ops,
+ .irq_chip = &pm8xxx_irq_chip,
.irq_handler = pm8xxx_irq_handler,
};
static const struct pm_irq_data pm8821_data = {
.num_irqs = PM8821_NR_IRQS,
- .irq_domain_ops = &pm8821_irq_domain_ops,
+ .irq_chip = &pm8821_irq_chip,
.irq_handler = pm8821_irq_handler,
};
@@ -571,14 +572,14 @@ static int pm8xxx_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, chip);
chip->regmap = regmap;
- chip->num_irqs = data->num_irqs;
- chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
+ chip->num_blocks = DIV_ROUND_UP(data->num_irqs, 8);
chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
+ chip->pm_irq_data = data;
spin_lock_init(&chip->pm_irq_lock);
chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
data->num_irqs,
- data->irq_domain_ops,
+ &pm8xxx_irq_domain_ops,
chip);
if (!chip->irqdomain)
return -ENODEV;
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index 93efbffcd3a4..2e66ab72c10b 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -150,6 +150,7 @@ config PINCTRL_QCOM_SSBI_PMIC
select PINMUX
select PINCONF
select GENERIC_PINCONF
+ select IRQ_DOMAIN_HIERARCHY
help
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips,
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
index ded7d765af2e..08dd62b5cebe 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
@@ -55,6 +55,8 @@
#define PM8XXX_MAX_GPIOS 44
+#define PM8XXX_GPIO_PHYSICAL_OFFSET 1
+
/* custom pinconf parameters */
#define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1)
#define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2)
@@ -99,6 +101,9 @@ struct pm8xxx_gpio {
struct pinctrl_desc desc;
unsigned npins;
+
+ struct fwnode_handle *fwnode;
+ struct irq_domain *domain;
};
static const struct pinconf_generic_params pm8xxx_gpio_bindings[] = {
@@ -499,11 +504,12 @@ static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) {
ret = pin->output_value;
- } else {
+ } else if (pin->irq >= 0) {
ret = irq_get_irqchip_state(pin->irq, IRQCHIP_STATE_LINE_LEVEL, &state);
if (!ret)
ret = !!state;
- }
+ } else
+ ret = -EINVAL;
return ret;
}
@@ -533,7 +539,7 @@ static int pm8xxx_gpio_of_xlate(struct gpio_chip *chip,
if (flags)
*flags = gpio_desc->args[1];
- return gpio_desc->args[0] - 1;
+ return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET;
}
@@ -541,8 +547,31 @@ static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
+ struct irq_fwspec fwspec;
+ int ret;
+
+ fwspec.fwnode = pctrl->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = offset + PM8XXX_GPIO_PHYSICAL_OFFSET;
+ fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
+
+ ret = irq_create_fwspec_mapping(&fwspec);
+
+ /*
+ * Cache the IRQ since pm8xxx_gpio_get() needs this to get determine the
+ * line level.
+ */
+ pin->irq = ret;
+
+ return ret;
+}
+
+static void pm8xxx_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
+ struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
- return pin->irq;
+ pin->irq = -1;
}
#ifdef CONFIG_DEBUG_FS
@@ -571,7 +600,7 @@ static void pm8xxx_gpio_dbg_show_one(struct seq_file *s,
"no", "high", "medium", "low"
};
- seq_printf(s, " gpio%-2d:", offset + 1);
+ seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET);
if (pin->disable) {
seq_puts(s, " ---");
} else {
@@ -603,6 +632,7 @@ static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
#endif
static const struct gpio_chip pm8xxx_gpio_template = {
+ .free = pm8xxx_gpio_free,
.direction_input = pm8xxx_gpio_direction_input,
.direction_output = pm8xxx_gpio_direction_output,
.get = pm8xxx_gpio_get,
@@ -664,13 +694,75 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl,
return 0;
}
+static struct irq_chip pm8xxx_irq_chip = {
+ .name = "ssbi-gpio",
+ .irq_mask_ack = irq_chip_mask_ack_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_set_type = irq_chip_set_type_parent,
+ .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
+};
+
+static int pm8xxx_domain_translate(struct irq_domain *domain,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
+{
+ struct pm8xxx_gpio *pctrl = container_of(domain->host_data,
+ struct pm8xxx_gpio, chip);
+
+ if (fwspec->param_count != 2 || fwspec->param[0] < 1 ||
+ fwspec->param[0] > pctrl->chip.ngpio)
+ return -EINVAL;
+
+ *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET;
+ *type = fwspec->param[1];
+
+ return 0;
+}
+
+static int pm8xxx_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct pm8xxx_gpio *pctrl = container_of(domain->host_data,
+ struct pm8xxx_gpio, chip);
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ int ret, i;
+
+ ret = pm8xxx_domain_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_info(domain, virq + i, hwirq + i,
+ &pm8xxx_irq_chip, pctrl, handle_level_irq,
+ NULL, NULL);
+
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ parent_fwspec.param_count = 2;
+ parent_fwspec.param[0] = hwirq + 0xc0;
+ parent_fwspec.param[1] = fwspec->param[1];
+
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
+}
+
+static const struct irq_domain_ops pm8xxx_domain_ops = {
+ .activate = gpiochip_irq_domain_activate,
+ .alloc = pm8xxx_domain_alloc,
+ .deactivate = gpiochip_irq_domain_deactivate,
+ .free = irq_domain_free_irqs_common,
+ .translate = pm8xxx_domain_translate,
+};
+
static const struct of_device_id pm8xxx_gpio_of_match[] = {
- { .compatible = "qcom,pm8018-gpio" },
- { .compatible = "qcom,pm8038-gpio" },
- { .compatible = "qcom,pm8058-gpio" },
- { .compatible = "qcom,pm8917-gpio" },
- { .compatible = "qcom,pm8921-gpio" },
- { .compatible = "qcom,ssbi-gpio" },
+ { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 },
+ { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 },
+ { .compatible = "qcom,pm8058-gpio", .data = (void *) 44 },
+ { .compatible = "qcom,pm8917-gpio", .data = (void *) 38 },
+ { .compatible = "qcom,pm8921-gpio", .data = (void *) 44 },
{ },
};
MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
@@ -678,22 +770,18 @@ MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
static int pm8xxx_gpio_probe(struct platform_device *pdev)
{
struct pm8xxx_pin_data *pin_data;
+ struct irq_domain *parent_domain;
+ struct device_node *parent_node;
struct pinctrl_pin_desc *pins;
struct pm8xxx_gpio *pctrl;
- int ret;
- int i, npins;
+ int ret, i;
pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
if (!pctrl)
return -ENOMEM;
pctrl->dev = &pdev->dev;
- npins = platform_irq_count(pdev);
- if (!npins)
- return -EINVAL;
- if (npins < 0)
- return npins;
- pctrl->npins = npins;
+ pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev);
pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!pctrl->regmap) {
@@ -720,12 +808,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
for (i = 0; i < pctrl->desc.npins; i++) {
pin_data[i].reg = SSBI_REG_ADDR_GPIO(i);
- pin_data[i].irq = platform_get_irq(pdev, i);
- if (pin_data[i].irq < 0) {
- dev_err(&pdev->dev,
- "missing interrupts for pin %d\n", i);
- return pin_data[i].irq;
- }
+ pin_data[i].irq = -1;
ret = pm8xxx_pin_populate(pctrl, &pin_data[i]);
if (ret)
@@ -756,10 +839,29 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
pctrl->chip.of_gpio_n_cells = 2;
pctrl->chip.label = dev_name(pctrl->dev);
pctrl->chip.ngpio = pctrl->npins;
+
+ parent_node = of_irq_find_parent(pctrl->dev->of_node);
+ if (!parent_node)
+ return -ENXIO;
+
+ parent_domain = irq_find_host(parent_node);
+ of_node_put(parent_node);
+ if (!parent_domain)
+ return -ENXIO;
+
+ pctrl->fwnode = of_node_to_fwnode(pctrl->dev->of_node);
+ pctrl->domain = irq_domain_create_hierarchy(parent_domain, 0,
+ pctrl->chip.ngpio,
+ pctrl->fwnode,
+ &pm8xxx_domain_ops,
+ &pctrl->chip);
+ if (!pctrl->domain)
+ return -ENODEV;
+
ret = gpiochip_add_data(&pctrl->chip, pctrl);
if (ret) {
dev_err(&pdev->dev, "failed register gpiochip\n");
- return ret;
+ goto err_chip_add_data;
}
/*
@@ -789,6 +891,8 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
unregister_gpiochip:
gpiochip_remove(&pctrl->chip);
+err_chip_add_data:
+ irq_domain_remove(pctrl->domain);
return ret;
}
@@ -798,6 +902,7 @@ static int pm8xxx_gpio_remove(struct platform_device *pdev)
struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev);
gpiochip_remove(&pctrl->chip);
+ irq_domain_remove(pctrl->domain);
return 0;
}
diff --git a/include/linux/irq.h b/include/linux/irq.h
index def2b2aac8b1..9a1a67d2e07d 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -605,6 +605,7 @@ extern void irq_chip_disable_parent(struct irq_data *data);
extern void irq_chip_ack_parent(struct irq_data *data);
extern int irq_chip_retrigger_hierarchy(struct irq_data *data);
extern void irq_chip_mask_parent(struct irq_data *data);
+extern void irq_chip_mask_ack_parent(struct irq_data *data);
extern void irq_chip_unmask_parent(struct irq_data *data);
extern void irq_chip_eoi_parent(struct irq_data *data);
extern int irq_chip_set_affinity_parent(struct irq_data *data,
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 35965f41d7be..fcefe0c7263f 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -419,6 +419,11 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type);
+int irq_domain_translate_twocell(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *out_hwirq,
+ unsigned int *out_type);
+
/* IPI functions */
int irq_reserve_ipi(struct irq_domain *domain, const struct cpumask *dest);
int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest);
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 086d5a34b5a0..0aefc2e69cf5 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -1278,6 +1278,17 @@ void irq_chip_mask_parent(struct irq_data *data)
EXPORT_SYMBOL_GPL(irq_chip_mask_parent);
/**
+ * irq_chip_mask_ack_parent - Mask and acknowledge the parent interrupt
+ * @data: Pointer to interrupt specific data
+ */
+void irq_chip_mask_ack_parent(struct irq_data *data)
+{
+ data = data->parent_data;
+ data->chip->irq_mask_ack(data);
+}
+EXPORT_SYMBOL_GPL(irq_chip_mask_ack_parent);
+
+/**
* irq_chip_unmask_parent - Unmask the parent interrupt
* @data: Pointer to interrupt specific data
*/
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 8b0be4bd6565..56a30d542b8e 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -729,16 +729,17 @@ static int irq_domain_translate(struct irq_domain *d,
return 0;
}
-static void of_phandle_args_to_fwspec(struct of_phandle_args *irq_data,
+static void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args,
+ unsigned int count,
struct irq_fwspec *fwspec)
{
int i;
- fwspec->fwnode = irq_data->np ? &irq_data->np->fwnode : NULL;
- fwspec->param_count = irq_data->args_count;
+ fwspec->fwnode = np ? &np->fwnode : NULL;
+ fwspec->param_count = count;
- for (i = 0; i < irq_data->args_count; i++)
- fwspec->param[i] = irq_data->args[i];
+ for (i = 0; i < count; i++)
+ fwspec->param[i] = args[i];
}
unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
@@ -836,7 +837,9 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
{
struct irq_fwspec fwspec;
- of_phandle_args_to_fwspec(irq_data, &fwspec);
+ of_phandle_args_to_fwspec(irq_data->np, irq_data->args,
+ irq_data->args_count, &fwspec);
+
return irq_create_fwspec_mapping(&fwspec);
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
@@ -928,11 +931,10 @@ int irq_domain_xlate_twocell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
- if (WARN_ON(intsize < 2))
- return -EINVAL;
- *out_hwirq = intspec[0];
- *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
- return 0;
+ struct irq_fwspec fwspec;
+
+ of_phandle_args_to_fwspec(ctrlr, intspec, intsize, &fwspec);
+ return irq_domain_translate_twocell(d, &fwspec, out_hwirq, out_type);
}
EXPORT_SYMBOL_GPL(irq_domain_xlate_twocell);
@@ -968,6 +970,27 @@ const struct irq_domain_ops irq_domain_simple_ops = {
};
EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
+/**
+ * irq_domain_translate_twocell() - Generic translate for direct two cell
+ * bindings
+ *
+ * Device Tree IRQ specifier translation function which works with two cell
+ * bindings where the cell values map directly to the hwirq number
+ * and linux irq flags.
+ */
+int irq_domain_translate_twocell(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ if (WARN_ON(fwspec->param_count < 2))
+ return -EINVAL;
+ *out_hwirq = fwspec->param[0];
+ *out_type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(irq_domain_translate_twocell);
+
int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq,
int node, const struct irq_affinity_desc *affinity)
{