Commit 43f68444 authored by Rohit Vaswani's avatar Rohit Vaswani Committed by David Brown

gpio: msm: Add device tree and irqdomain support for gpio-msm-v2

This cleans up the gpio-msm-v2 driver of all the global define usage.
The number of gpios are now defined in the device tree. This enables
adding irqdomain support as well.
Signed-off-by: default avatarRohit Vaswani <rvaswani@codeaurora.org>
Acked-by: default avatarGrant Likely <grant.likely@linaro.org>
Signed-off-by: default avatarDavid Brown <davidb@codeaurora.org>
parent eda9dcfa
MSM GPIO controller bindings
Required properties:
- compatible:
- "qcom,msm-gpio" for MSM controllers
- #gpio-cells : Should be two.
- first cell is the pin number
- second cell is used to specify optional parameters (unused)
- gpio-controller : Marks the device node as a GPIO controller.
- #interrupt-cells : Should be 2.
- interrupt-controller: Mark the device node as an interrupt controller
- interrupts : Specify the TLMM summary interrupt number
- ngpio : Specify the number of MSM GPIOs
Example:
msmgpio: gpio@fd510000 {
compatible = "qcom,msm-gpio";
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0xfd510000 0x4000>;
interrupts = <0 208 0>;
ngpio = <150>;
};
...@@ -26,6 +26,17 @@ timer@2000004 { ...@@ -26,6 +26,17 @@ timer@2000004 {
cpu-offset = <0x40000>; cpu-offset = <0x40000>;
}; };
msmgpio: gpio@800000 {
compatible = "qcom,msm-gpio";
reg = <0x00800000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
ngpio = <173>;
interrupts = <0 32 0x4>;
interrupt-controller;
#interrupt-cells = <2>;
};
serial@19c400000 { serial@19c400000 {
compatible = "qcom,msm-hsuart", "qcom,msm-uart"; compatible = "qcom,msm-hsuart", "qcom,msm-uart";
reg = <0x19c40000 0x1000>, reg = <0x19c40000 0x1000>,
......
...@@ -26,6 +26,17 @@ timer@200a000 { ...@@ -26,6 +26,17 @@ timer@200a000 {
cpu-offset = <0x80000>; cpu-offset = <0x80000>;
}; };
msmgpio: gpio@fd510000 {
compatible = "qcom,msm-gpio";
gpio-controller;
#gpio-cells = <2>;
ngpio = <150>;
interrupts = <0 32 0x4>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0xfd510000 0x4000>;
};
serial@19c400000 { serial@19c400000 {
compatible = "qcom,msm-hsuart", "qcom,msm-uart"; compatible = "qcom,msm-hsuart", "qcom,msm-uart";
reg = <0x16440000 0x1000>, reg = <0x16440000 0x1000>,
......
...@@ -165,7 +165,7 @@ config GPIO_MSM_V1 ...@@ -165,7 +165,7 @@ config GPIO_MSM_V1
config GPIO_MSM_V2 config GPIO_MSM_V2
tristate "Qualcomm MSM GPIO v2" tristate "Qualcomm MSM GPIO v2"
depends on GPIOLIB && ARCH_MSM depends on GPIOLIB && OF && ARCH_MSM
help help
Say yes here to support the GPIO interface on ARM v7 based Say yes here to support the GPIO interface on ARM v7 based
Qualcomm MSM chips. Most of the pins on the MSM can be Qualcomm MSM chips. Most of the pins on the MSM can be
......
...@@ -19,17 +19,21 @@ ...@@ -19,17 +19,21 @@
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/err.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/slab.h>
#include <mach/msm_iomap.h> #define MAX_NR_GPIO 300
/* Bits of interest in the GPIO_IN_OUT register. /* Bits of interest in the GPIO_IN_OUT register.
*/ */
...@@ -76,13 +80,6 @@ enum { ...@@ -76,13 +80,6 @@ enum {
TARGET_PROC_NONE = 7, TARGET_PROC_NONE = 7,
}; };
#define GPIO_INTR_CFG_SU(gpio) (MSM_TLMM_BASE + 0x0400 + (0x04 * (gpio)))
#define GPIO_CONFIG(gpio) (MSM_TLMM_BASE + 0x1000 + (0x10 * (gpio)))
#define GPIO_IN_OUT(gpio) (MSM_TLMM_BASE + 0x1004 + (0x10 * (gpio)))
#define GPIO_INTR_CFG(gpio) (MSM_TLMM_BASE + 0x1008 + (0x10 * (gpio)))
#define GPIO_INTR_STATUS(gpio) (MSM_TLMM_BASE + 0x100c + (0x10 * (gpio)))
/** /**
* struct msm_gpio_dev: the MSM8660 SoC GPIO device structure * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure
* *
...@@ -101,11 +98,27 @@ enum { ...@@ -101,11 +98,27 @@ enum {
*/ */
struct msm_gpio_dev { struct msm_gpio_dev {
struct gpio_chip gpio_chip; struct gpio_chip gpio_chip;
DECLARE_BITMAP(enabled_irqs, NR_GPIO_IRQS); DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO);
DECLARE_BITMAP(wake_irqs, NR_GPIO_IRQS); DECLARE_BITMAP(wake_irqs, MAX_NR_GPIO);
DECLARE_BITMAP(dual_edge_irqs, NR_GPIO_IRQS); DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
struct irq_domain *domain;
unsigned int summary_irq;
void __iomem *msm_tlmm_base;
}; };
struct msm_gpio_dev msm_gpio;
#define GPIO_INTR_CFG_SU(gpio) (msm_gpio.msm_tlmm_base + 0x0400 + \
(0x04 * (gpio)))
#define GPIO_CONFIG(gpio) (msm_gpio.msm_tlmm_base + 0x1000 + \
(0x10 * (gpio)))
#define GPIO_IN_OUT(gpio) (msm_gpio.msm_tlmm_base + 0x1004 + \
(0x10 * (gpio)))
#define GPIO_INTR_CFG(gpio) (msm_gpio.msm_tlmm_base + 0x1008 + \
(0x10 * (gpio)))
#define GPIO_INTR_STATUS(gpio) (msm_gpio.msm_tlmm_base + 0x100c + \
(0x10 * (gpio)))
static DEFINE_SPINLOCK(tlmm_lock); static DEFINE_SPINLOCK(tlmm_lock);
static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip) static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip)
...@@ -168,27 +181,19 @@ static void msm_gpio_free(struct gpio_chip *chip, unsigned offset) ...@@ -168,27 +181,19 @@ static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset) static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{ {
return MSM_GPIO_TO_INT(chip->base + offset); struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
struct irq_domain *domain = g_dev->domain;
return irq_create_mapping(domain, offset);
} }
static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq) static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
{ {
return irq - MSM_GPIO_TO_INT(chip->base); struct irq_data *irq_data = irq_get_irq_data(irq);
return irq_data->hwirq;
} }
static struct msm_gpio_dev msm_gpio = {
.gpio_chip = {
.base = 0,
.ngpio = NR_GPIO_IRQS,
.direction_input = msm_gpio_direction_input,
.direction_output = msm_gpio_direction_output,
.get = msm_gpio_get,
.set = msm_gpio_set,
.to_irq = msm_gpio_to_irq,
.request = msm_gpio_request,
.free = msm_gpio_free,
},
};
/* For dual-edge interrupts in software, since the hardware has no /* For dual-edge interrupts in software, since the hardware has no
* such support: * such support:
...@@ -226,9 +231,9 @@ static void msm_gpio_update_dual_edge_pos(unsigned gpio) ...@@ -226,9 +231,9 @@ static void msm_gpio_update_dual_edge_pos(unsigned gpio)
if (intstat || val == val2) if (intstat || val == val2)
return; return;
} while (loop_limit-- > 0); } while (loop_limit-- > 0);
pr_err("dual-edge irq failed to stabilize, " pr_err("%s: dual-edge irq failed to stabilize, "
"interrupts dropped. %#08x != %#08x\n", "interrupts dropped. %#08x != %#08x\n",
val, val2); __func__, val, val2);
} }
static void msm_gpio_irq_ack(struct irq_data *d) static void msm_gpio_irq_ack(struct irq_data *d)
...@@ -315,10 +320,10 @@ static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc) ...@@ -315,10 +320,10 @@ static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc)
chained_irq_enter(chip, desc); chained_irq_enter(chip, desc);
for_each_set_bit(i, msm_gpio.enabled_irqs, NR_GPIO_IRQS) { for_each_set_bit(i, msm_gpio.enabled_irqs, MAX_NR_GPIO) {
if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS)) if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS))
generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip, generic_handle_irq(irq_find_mapping(msm_gpio.domain,
i)); i));
} }
chained_irq_exit(chip, desc); chained_irq_exit(chip, desc);
...@@ -329,13 +334,13 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) ...@@ -329,13 +334,13 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq); int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
if (on) { if (on) {
if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS)) if (bitmap_empty(msm_gpio.wake_irqs, MAX_NR_GPIO))
irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 1); irq_set_irq_wake(msm_gpio.summary_irq, 1);
set_bit(gpio, msm_gpio.wake_irqs); set_bit(gpio, msm_gpio.wake_irqs);
} else { } else {
clear_bit(gpio, msm_gpio.wake_irqs); clear_bit(gpio, msm_gpio.wake_irqs);
if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS)) if (bitmap_empty(msm_gpio.wake_irqs, MAX_NR_GPIO))
irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 0); irq_set_irq_wake(msm_gpio.summary_irq, 0);
} }
return 0; return 0;
...@@ -350,30 +355,86 @@ static struct irq_chip msm_gpio_irq_chip = { ...@@ -350,30 +355,86 @@ static struct irq_chip msm_gpio_irq_chip = {
.irq_set_wake = msm_gpio_irq_set_wake, .irq_set_wake = msm_gpio_irq_set_wake,
}; };
static int msm_gpio_probe(struct platform_device *dev) static struct lock_class_key msm_gpio_lock_class;
static int msm_gpio_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
irq_set_lockdep_class(irq, &msm_gpio_lock_class);
irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
handle_level_irq);
set_irq_flags(irq, IRQF_VALID);
return 0;
}
static const struct irq_domain_ops msm_gpio_irq_domain_ops = {
.xlate = irq_domain_xlate_twocell,
.map = msm_gpio_irq_domain_map,
};
static int msm_gpio_probe(struct platform_device *pdev)
{ {
int i, irq, ret; int ret, ngpio;
struct resource *res;
if (!of_property_read_u32(pdev->dev.of_node, "ngpio", &ngpio)) {
dev_err(&pdev->dev, "%s: ngpio property missing\n", __func__);
return -EINVAL;
}
if (ngpio > MAX_NR_GPIO)
WARN(1, "ngpio exceeds the MAX_NR_GPIO. Increase MAX_NR_GPIO\n");
bitmap_zero(msm_gpio.enabled_irqs, MAX_NR_GPIO);
bitmap_zero(msm_gpio.wake_irqs, MAX_NR_GPIO);
bitmap_zero(msm_gpio.dual_edge_irqs, MAX_NR_GPIO);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
msm_gpio.msm_tlmm_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(msm_gpio.msm_tlmm_base))
return PTR_ERR(msm_gpio.msm_tlmm_base);
msm_gpio.gpio_chip.ngpio = ngpio;
msm_gpio.gpio_chip.label = pdev->name;
msm_gpio.gpio_chip.dev = &pdev->dev;
msm_gpio.gpio_chip.base = 0;
msm_gpio.gpio_chip.direction_input = msm_gpio_direction_input;
msm_gpio.gpio_chip.direction_output = msm_gpio_direction_output;
msm_gpio.gpio_chip.get = msm_gpio_get;
msm_gpio.gpio_chip.set = msm_gpio_set;
msm_gpio.gpio_chip.to_irq = msm_gpio_to_irq;
msm_gpio.gpio_chip.request = msm_gpio_request;
msm_gpio.gpio_chip.free = msm_gpio_free;
bitmap_zero(msm_gpio.enabled_irqs, NR_GPIO_IRQS);
bitmap_zero(msm_gpio.wake_irqs, NR_GPIO_IRQS);
bitmap_zero(msm_gpio.dual_edge_irqs, NR_GPIO_IRQS);
msm_gpio.gpio_chip.label = dev->name;
ret = gpiochip_add(&msm_gpio.gpio_chip); ret = gpiochip_add(&msm_gpio.gpio_chip);
if (ret < 0) if (ret < 0) {
dev_err(&pdev->dev, "gpiochip_add failed with error %d\n", ret);
return ret; return ret;
}
for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) { msm_gpio.summary_irq = platform_get_irq(pdev, 0);
irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i); if (msm_gpio.summary_irq < 0) {
irq_set_chip_and_handler(irq, &msm_gpio_irq_chip, dev_err(&pdev->dev, "No Summary irq defined for msmgpio\n");
handle_level_irq); return msm_gpio.summary_irq;
set_irq_flags(irq, IRQF_VALID);
} }
irq_set_chained_handler(TLMM_SCSS_SUMMARY_IRQ, msm_gpio.domain = irq_domain_add_linear(pdev->dev.of_node, ngpio,
msm_summary_irq_handler); &msm_gpio_irq_domain_ops,
&msm_gpio);
if (!msm_gpio.domain)
return -ENODEV;
irq_set_chained_handler(msm_gpio.summary_irq, msm_summary_irq_handler);
return 0; return 0;
} }
static struct of_device_id msm_gpio_of_match[] = {
{ .compatible = "qcom,msm-gpio", },
{ },
};
static int msm_gpio_remove(struct platform_device *dev) static int msm_gpio_remove(struct platform_device *dev)
{ {
int ret = gpiochip_remove(&msm_gpio.gpio_chip); int ret = gpiochip_remove(&msm_gpio.gpio_chip);
...@@ -381,7 +442,7 @@ static int msm_gpio_remove(struct platform_device *dev) ...@@ -381,7 +442,7 @@ static int msm_gpio_remove(struct platform_device *dev)
if (ret < 0) if (ret < 0)
return ret; return ret;
irq_set_handler(TLMM_SCSS_SUMMARY_IRQ, NULL); irq_set_handler(msm_gpio.summary_irq, NULL);
return 0; return 0;
} }
...@@ -392,36 +453,11 @@ static struct platform_driver msm_gpio_driver = { ...@@ -392,36 +453,11 @@ static struct platform_driver msm_gpio_driver = {
.driver = { .driver = {
.name = "msmgpio", .name = "msmgpio",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = msm_gpio_of_match,
}, },
}; };
static struct platform_device msm_device_gpio = { module_platform_driver(msm_gpio_driver)
.name = "msmgpio",
.id = -1,
};
static int __init msm_gpio_init(void)
{
int rc;
rc = platform_driver_register(&msm_gpio_driver);
if (!rc) {
rc = platform_device_register(&msm_device_gpio);
if (rc)
platform_driver_unregister(&msm_gpio_driver);
}
return rc;
}
static void __exit msm_gpio_exit(void)
{
platform_device_unregister(&msm_device_gpio);
platform_driver_unregister(&msm_gpio_driver);
}
postcore_initcall(msm_gpio_init);
module_exit(msm_gpio_exit);
MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>"); MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs"); MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
......
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