diff mbox

[PATCHv2,3/3] gpio: sch: Enable IRQ support for Quark X1000

Message ID 1411726047-3224-4-git-send-email-rebecca.swee.fun.chang@intel.com
State Not Applicable, archived
Headers show

Commit Message

Chang Rebecca Swee Fun Sept. 26, 2014, 10:07 a.m. UTC
Intel Quark X1000 GPIO controller supports interrupt handling for
both core power well and resume power well. This patch is to enable
the IRQ support and provide IRQ handling for Intel Quark X1000
GPIO-SCH device driver.

This piece of work is derived from Dan O'Donovan's initial work for
Quark X1000 enabling.

Signed-off-by: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
---
 drivers/gpio/gpio-sch.c |  267 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 253 insertions(+), 14 deletions(-)

Comments

Linus Walleij Oct. 15, 2014, 7:12 a.m. UTC | #1
On Fri, Sep 26, 2014 at 12:07 PM, Chang Rebecca Swee Fun
<rebecca.swee.fun.chang@intel.com> wrote:

> Intel Quark X1000 GPIO controller supports interrupt handling for
> both core power well and resume power well. This patch is to enable
> the IRQ support and provide IRQ handling for Intel Quark X1000
> GPIO-SCH device driver.
>
> This piece of work is derived from Dan O'Donovan's initial work for
> Quark X1000 enabling.
>
> Signed-off-by: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
(...)

This patch needs to be rebased on the gpio git "devel" branch or
Torvalds' HEAD before I can apply it.

>  #define GEN    0x00
>  #define GIO    0x04
>  #define GLV    0x08
> +#define GTPE   0x0C
> +#define GTNE   0x10
> +#define GGPE   0x14
> +#define GSMI   0x18
> +#define GTS    0x1C
> +#define CGNMIEN        0x40
> +#define RGNMIEN        0x44

So the initial SCH driver for the Intel Poulsbo was submitted by Denis
Turischev in 2010.

Does these registers exist and work on the Poulsbo as well?

Is it really enough to distinguish between these variants by
checking if we're getting an IRQ resource on the device or not?
Is there some version register or so?

>  struct sch_gpio {
>         struct gpio_chip chip;
> +       struct irq_data data;
>         spinlock_t lock;
>         unsigned short iobase;
>         unsigned short core_base;
>         unsigned short resume_base;
> +       int irq_base;
> +       int irq_num;
> +       int irq_support;

Isn't that a bool?

> +       irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +       sch->irq_support = !!irq;

Yeah, it's a bool....

> +       if (sch->irq_support) {
> +               sch->irq_num = irq->start;
> +               if (sch->irq_num < 0) {
> +                       dev_warn(&pdev->dev,
> +                                "failed to obtain irq number for device\n");
> +                       sch->irq_support = 0;

= false;

> +       if (sch->irq_support) {
> +               sch->irq_base = irq_alloc_descs(-1, 0, sch->chip.ngpio,
> +                                               NUMA_NO_NODE);
> +               if (sch->irq_base < 0) {
> +                       dev_err(&pdev->dev,
> +                               "failed to add GPIO IRQ descs\n");

Failed to *allocate* actually...

> +                       sch->irq_base = -1;

This is overzealous. Drop it.

> +                       goto err_sch_intr_chip;

You're bailing out anyway, see.

>  static int sch_gpio_remove(struct platform_device *pdev)
>  {
>         struct sch_gpio *sch = platform_get_drvdata(pdev);
> +       int err;
>
> -       gpiochip_remove(&sch->chip);
> -       return 0;
> +       if (sch->irq_support) {
> +               sch_gpio_irqs_deinit(sch, sch->chip.ngpio);
> +
> +               if (sch->irq_num >= 0)
> +                       free_irq(sch->irq_num, sch);
> +
> +               irq_free_descs(sch->irq_base, sch->chip.ngpio);
> +       }
> +
> +       err = gpiochip_remove(&sch->chip);
> +       if (err)
> +               dev_err(&pdev->dev,
> +                       "%s gpiochip_remove() failed\n", __func__);

So gpiochip_remove() does *NOT* return an error in the current
kernel. We just removed that return value from the SCH driver in the
previous cycle for the reason that we were killing off the return type.
commit 9f5132ae82fdbb047cc187bf689a81c8cc0de7fa
"gpio: remove all usage of gpio_remove retval in driver/gpio"

So don't reintroduce stuff we're actively trying to get rid of.

Apart from this is looks OK, Mika can you ACK the end result?

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Chang Rebecca Swee Fun Oct. 15, 2014, 9:20 a.m. UTC | #2
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogTGludXMgV2FsbGVpaiBb
bWFpbHRvOmxpbnVzLndhbGxlaWpAbGluYXJvLm9yZ10NCj4gU2VudDogMTUgT2N0b2JlciwgMjAx
NCAzOjEzIFBNDQo+IFRvOiBDaGFuZywgUmViZWNjYSBTd2VlIEZ1bjsgRGVuaXMgVHVyaXNjaGV2
DQo+IENjOiBXZXN0ZXJiZXJnLCBNaWthOyBHUElPIFN1YnN5c3RlbSBNYWlsaW5nIExpc3Q7IExp
bnV4IEtlcm5lbCBNYWlsaW5nIExpc3QNCj4gU3ViamVjdDogUmU6IFtQQVRDSHYyIDMvM10gZ3Bp
bzogc2NoOiBFbmFibGUgSVJRIHN1cHBvcnQgZm9yIFF1YXJrIFgxMDAwDQo+IA0KPiBPbiBGcmks
IFNlcCAyNiwgMjAxNCBhdCAxMjowNyBQTSwgQ2hhbmcgUmViZWNjYSBTd2VlIEZ1bg0KPiA8cmVi
ZWNjYS5zd2VlLmZ1bi5jaGFuZ0BpbnRlbC5jb20+IHdyb3RlOg0KPiANCj4gPiBJbnRlbCBRdWFy
ayBYMTAwMCBHUElPIGNvbnRyb2xsZXIgc3VwcG9ydHMgaW50ZXJydXB0IGhhbmRsaW5nIGZvciBi
b3RoDQo+ID4gY29yZSBwb3dlciB3ZWxsIGFuZCByZXN1bWUgcG93ZXIgd2VsbC4gVGhpcyBwYXRj
aCBpcyB0byBlbmFibGUgdGhlIElSUQ0KPiA+IHN1cHBvcnQgYW5kIHByb3ZpZGUgSVJRIGhhbmRs
aW5nIGZvciBJbnRlbCBRdWFyayBYMTAwMCBHUElPLVNDSCBkZXZpY2UNCj4gPiBkcml2ZXIuDQo+
ID4NCj4gPiBUaGlzIHBpZWNlIG9mIHdvcmsgaXMgZGVyaXZlZCBmcm9tIERhbiBPJ0Rvbm92YW4n
cyBpbml0aWFsIHdvcmsgZm9yDQo+ID4gUXVhcmsgWDEwMDAgZW5hYmxpbmcuDQo+ID4NCj4gPiBT
aWduZWQtb2ZmLWJ5OiBDaGFuZyBSZWJlY2NhIFN3ZWUgRnVuDQo+ID4gPHJlYmVjY2Euc3dlZS5m
dW4uY2hhbmdAaW50ZWwuY29tPg0KPiAoLi4uKQ0KPiANCj4gVGhpcyBwYXRjaCBuZWVkcyB0byBi
ZSByZWJhc2VkIG9uIHRoZSBncGlvIGdpdCAiZGV2ZWwiIGJyYW5jaCBvciBUb3J2YWxkcycNCj4g
SEVBRCBiZWZvcmUgSSBjYW4gYXBwbHkgaXQuDQoNCkkgd2lsbCByZWJhc2UgYW5kIHJlc2VuZCB3
aXRoIHRoZSBmaXhlcyBiZWxvdy4NCg0KPiANCj4gPiAgI2RlZmluZSBHRU4gICAgMHgwMA0KPiA+
ICAjZGVmaW5lIEdJTyAgICAweDA0DQo+ID4gICNkZWZpbmUgR0xWICAgIDB4MDgNCj4gPiArI2Rl
ZmluZSBHVFBFICAgMHgwQw0KPiA+ICsjZGVmaW5lIEdUTkUgICAweDEwDQo+ID4gKyNkZWZpbmUg
R0dQRSAgIDB4MTQNCj4gPiArI2RlZmluZSBHU01JICAgMHgxOA0KPiA+ICsjZGVmaW5lIEdUUyAg
ICAweDFDDQo+ID4gKyNkZWZpbmUgQ0dOTUlFTiAgICAgICAgMHg0MA0KPiA+ICsjZGVmaW5lIFJH
Tk1JRU4gICAgICAgIDB4NDQNCj4gDQo+IFNvIHRoZSBpbml0aWFsIFNDSCBkcml2ZXIgZm9yIHRo
ZSBJbnRlbCBQb3Vsc2JvIHdhcyBzdWJtaXR0ZWQgYnkgRGVuaXMgVHVyaXNjaGV2DQo+IGluIDIw
MTAuDQo+IA0KPiBEb2VzIHRoZXNlIHJlZ2lzdGVycyBleGlzdCBhbmQgd29yayBvbiB0aGUgUG91
bHNibyBhcyB3ZWxsPw0KPiANCj4gSXMgaXQgcmVhbGx5IGVub3VnaCB0byBkaXN0aW5ndWlzaCBi
ZXR3ZWVuIHRoZXNlIHZhcmlhbnRzIGJ5IGNoZWNraW5nIGlmIHdlJ3JlDQo+IGdldHRpbmcgYW4g
SVJRIHJlc291cmNlIG9uIHRoZSBkZXZpY2Ugb3Igbm90Pw0KPiBJcyB0aGVyZSBzb21lIHZlcnNp
b24gcmVnaXN0ZXIgb3Igc28/DQoNClRoZSByZWdpc3RlciB2YWx1ZXMgZGVmaW5lZCBoZXJlIGFy
ZSBvZmZzZXQgdmFsdWUsIHRoZXkgYXJlIG5vdCB0aGUgZXhhY3QgcmVnaXN0ZXIgYWRkcmVzcy4g
DQpUaGV5IGFyZSBub3QgdmVyc2lvbiByZWdpc3RlciBhcyBpdCBqdXN0IGNhcnJpZXMgYSByZWdp
c3RlciBvZmZzZXQgdmFsdWUuDQoNCj4gPiAgc3RydWN0IHNjaF9ncGlvIHsNCj4gPiAgICAgICAg
IHN0cnVjdCBncGlvX2NoaXAgY2hpcDsNCj4gPiArICAgICAgIHN0cnVjdCBpcnFfZGF0YSBkYXRh
Ow0KPiA+ICAgICAgICAgc3BpbmxvY2tfdCBsb2NrOw0KPiA+ICAgICAgICAgdW5zaWduZWQgc2hv
cnQgaW9iYXNlOw0KPiA+ICAgICAgICAgdW5zaWduZWQgc2hvcnQgY29yZV9iYXNlOw0KPiA+ICAg
ICAgICAgdW5zaWduZWQgc2hvcnQgcmVzdW1lX2Jhc2U7DQo+ID4gKyAgICAgICBpbnQgaXJxX2Jh
c2U7DQo+ID4gKyAgICAgICBpbnQgaXJxX251bTsNCj4gPiArICAgICAgIGludCBpcnFfc3VwcG9y
dDsNCj4gDQo+IElzbid0IHRoYXQgYSBib29sPw0KPiANCj4gPiArICAgICAgIGlycSA9IHBsYXRm
b3JtX2dldF9yZXNvdXJjZShwZGV2LCBJT1JFU09VUkNFX0lSUSwgMCk7DQo+ID4gKyAgICAgICBz
Y2gtPmlycV9zdXBwb3J0ID0gISFpcnE7DQo+IA0KPiBZZWFoLCBpdCdzIGEgYm9vbC4uLi4NCg0K
SSB3aWxsIGNoYW5nZSBpdCB0byBib29sLg0KDQo+IA0KPiA+ICsgICAgICAgaWYgKHNjaC0+aXJx
X3N1cHBvcnQpIHsNCj4gPiArICAgICAgICAgICAgICAgc2NoLT5pcnFfbnVtID0gaXJxLT5zdGFy
dDsNCj4gPiArICAgICAgICAgICAgICAgaWYgKHNjaC0+aXJxX251bSA8IDApIHsNCj4gPiArICAg
ICAgICAgICAgICAgICAgICAgICBkZXZfd2FybigmcGRldi0+ZGV2LA0KPiA+ICsgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICJmYWlsZWQgdG8gb2J0YWluIGlycSBudW1iZXIgZm9yIGRl
dmljZVxuIik7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgc2NoLT5pcnFfc3VwcG9ydCA9
IDA7DQo+IA0KPiA9IGZhbHNlOw0KDQpOb3RlZA0KPiANCj4gPiArICAgICAgIGlmIChzY2gtPmly
cV9zdXBwb3J0KSB7DQo+ID4gKyAgICAgICAgICAgICAgIHNjaC0+aXJxX2Jhc2UgPSBpcnFfYWxs
b2NfZGVzY3MoLTEsIDAsIHNjaC0+Y2hpcC5uZ3BpbywNCj4gPiArICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOVU1BX05PX05PREUpOw0KPiA+ICsgICAgICAg
ICAgICAgICBpZiAoc2NoLT5pcnFfYmFzZSA8IDApIHsNCj4gPiArICAgICAgICAgICAgICAgICAg
ICAgICBkZXZfZXJyKCZwZGV2LT5kZXYsDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAiZmFpbGVkIHRvIGFkZCBHUElPIElSUSBkZXNjc1xuIik7DQo+IA0KPiBGYWlsZWQgdG8g
KmFsbG9jYXRlKiBhY3R1YWxseS4uLg0KPiANCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBz
Y2gtPmlycV9iYXNlID0gLTE7DQo+IA0KPiBUaGlzIGlzIG92ZXJ6ZWFsb3VzLiBEcm9wIGl0Lg0K
PiANCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBnb3RvIGVycl9zY2hfaW50cl9jaGlwOw0K
PiANCj4gWW91J3JlIGJhaWxpbmcgb3V0IGFueXdheSwgc2VlLg0KDQpOb3RlZC4gSSB3aWxsIGNo
YW5nZSB0aGUgcGhyYXNlIGFjY29yZGluZ2x5IGFuZCByZW1vdmUgdGhlIGV4cHJlc3Npb24gb24g
bmV4dCBzdWJtaXNzaW9uLg0KDQo+IA0KPiA+ICBzdGF0aWMgaW50IHNjaF9ncGlvX3JlbW92ZShz
dHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KSAgew0KPiA+ICAgICAgICAgc3RydWN0IHNjaF9n
cGlvICpzY2ggPSBwbGF0Zm9ybV9nZXRfZHJ2ZGF0YShwZGV2KTsNCj4gPiArICAgICAgIGludCBl
cnI7DQo+ID4NCj4gPiAtICAgICAgIGdwaW9jaGlwX3JlbW92ZSgmc2NoLT5jaGlwKTsNCj4gPiAt
ICAgICAgIHJldHVybiAwOw0KPiA+ICsgICAgICAgaWYgKHNjaC0+aXJxX3N1cHBvcnQpIHsNCj4g
PiArICAgICAgICAgICAgICAgc2NoX2dwaW9faXJxc19kZWluaXQoc2NoLCBzY2gtPmNoaXAubmdw
aW8pOw0KPiA+ICsNCj4gPiArICAgICAgICAgICAgICAgaWYgKHNjaC0+aXJxX251bSA+PSAwKQ0K
PiA+ICsgICAgICAgICAgICAgICAgICAgICAgIGZyZWVfaXJxKHNjaC0+aXJxX251bSwgc2NoKTsN
Cj4gPiArDQo+ID4gKyAgICAgICAgICAgICAgIGlycV9mcmVlX2Rlc2NzKHNjaC0+aXJxX2Jhc2Us
IHNjaC0+Y2hpcC5uZ3Bpbyk7DQo+ID4gKyAgICAgICB9DQo+ID4gKw0KPiA+ICsgICAgICAgZXJy
ID0gZ3Bpb2NoaXBfcmVtb3ZlKCZzY2gtPmNoaXApOw0KPiA+ICsgICAgICAgaWYgKGVycikNCj4g
PiArICAgICAgICAgICAgICAgZGV2X2VycigmcGRldi0+ZGV2LA0KPiA+ICsgICAgICAgICAgICAg
ICAgICAgICAgICIlcyBncGlvY2hpcF9yZW1vdmUoKSBmYWlsZWRcbiIsIF9fZnVuY19fKTsNCj4g
DQo+IFNvIGdwaW9jaGlwX3JlbW92ZSgpIGRvZXMgKk5PVCogcmV0dXJuIGFuIGVycm9yIGluIHRo
ZSBjdXJyZW50DQo+IGtlcm5lbC4gV2UganVzdCByZW1vdmVkIHRoYXQgcmV0dXJuIHZhbHVlIGZy
b20gdGhlIFNDSCBkcml2ZXIgaW4gdGhlDQo+IHByZXZpb3VzIGN5Y2xlIGZvciB0aGUgcmVhc29u
IHRoYXQgd2Ugd2VyZSBraWxsaW5nIG9mZiB0aGUgcmV0dXJuIHR5cGUuDQo+IGNvbW1pdCA5ZjUx
MzJhZTgyZmRiYjA0N2NjMTg3YmY2ODlhODFjOGNjMGRlN2ZhDQo+ICJncGlvOiByZW1vdmUgYWxs
IHVzYWdlIG9mIGdwaW9fcmVtb3ZlIHJldHZhbCBpbiBkcml2ZXIvZ3BpbyINCj4gDQo+IFNvIGRv
bid0IHJlaW50cm9kdWNlIHN0dWZmIHdlJ3JlIGFjdGl2ZWx5IHRyeWluZyB0byBnZXQgcmlkIG9m
Lg0KPiANCj4gQXBhcnQgZnJvbSB0aGlzIGlzIGxvb2tzIE9LLCBNaWthIGNhbiB5b3UgQUNLIHRo
ZSBlbmQgcmVzdWx0Pw0KDQpOb3RlZCB3aXRoIHRoYW5rcy4gSSB3aWxsIGRvIHRoZSBjaGFuZ2Vz
IHJlcXVpcmVkIGFuZCByZXNlbmQgdGhlIHNlcmllcy4NClRoYW5rcy4NCg0KUmVnYXJkcw0KUmVi
ZWNjYQ0K
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
index 952990f..332ffaf 100644
--- a/drivers/gpio/gpio-sch.c
+++ b/drivers/gpio/gpio-sch.c
@@ -28,17 +28,30 @@ 
 #include <linux/pci_ids.h>
 
 #include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
 
 #define GEN	0x00
 #define GIO	0x04
 #define GLV	0x08
+#define GTPE	0x0C
+#define GTNE	0x10
+#define GGPE	0x14
+#define GSMI	0x18
+#define GTS	0x1C
+#define CGNMIEN	0x40
+#define RGNMIEN	0x44
 
 struct sch_gpio {
 	struct gpio_chip chip;
+	struct irq_data data;
 	spinlock_t lock;
 	unsigned short iobase;
 	unsigned short core_base;
 	unsigned short resume_base;
+	int irq_base;
+	int irq_num;
+	int irq_support;
 };
 
 #define to_sch_gpio(c)	container_of(c, struct sch_gpio, chip)
@@ -66,10 +79,11 @@  static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio)
 static void sch_gpio_register_set(struct sch_gpio *sch, unsigned gpio,
 				  unsigned reg)
 {
+	unsigned long flags;
 	unsigned short offset, bit;
 	u8 enable;
 
-	spin_lock(&sch->lock);
+	spin_lock_irqsave(&sch->lock, flags);
 
 	offset = sch_gpio_offset(sch, gpio, reg);
 	bit = sch_gpio_bit(sch, gpio);
@@ -78,16 +92,17 @@  static void sch_gpio_register_set(struct sch_gpio *sch, unsigned gpio,
 	if (!(enable & BIT(bit)))
 		outb(enable | BIT(bit), sch->iobase + offset);
 
-	spin_unlock(&sch->lock);
+	spin_unlock_irqrestore(&sch->lock, flags);
 }
 
 static void sch_gpio_register_clear(struct sch_gpio *sch, unsigned gpio,
 				    unsigned reg)
 {
+	unsigned long flags;
 	unsigned short offset, bit;
 	u8 disable;
 
-	spin_lock(&sch->lock);
+	spin_lock_irqsave(&sch->lock, flags);
 
 	offset = sch_gpio_offset(sch, gpio, reg);
 	bit = sch_gpio_bit(sch, gpio);
@@ -96,7 +111,7 @@  static void sch_gpio_register_clear(struct sch_gpio *sch, unsigned gpio,
 	if (disable & BIT(bit))
 		outb(disable & ~BIT(bit), sch->iobase + offset);
 
-	spin_unlock(&sch->lock);
+	spin_unlock_irqrestore(&sch->lock, flags);
 }
 
 static int sch_gpio_reg_get(struct gpio_chip *gc, unsigned gpio, unsigned reg)
@@ -134,10 +149,11 @@  static void sch_gpio_reg_set(struct gpio_chip *gc, unsigned gpio, unsigned reg,
 static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
 {
 	struct sch_gpio *sch = to_sch_gpio(gc);
+	unsigned long flags;
 
-	spin_lock(&sch->lock);
+	spin_lock_irqsave(&sch->lock, flags);
 	sch_gpio_register_set(sch, gpio_num, GIO);
-	spin_unlock(&sch->lock);
+	spin_unlock_irqrestore(&sch->lock, flags);
 	return 0;
 }
 
@@ -149,20 +165,22 @@  static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
 static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val)
 {
 	struct sch_gpio *sch = to_sch_gpio(gc);
+	unsigned long flags;
 
-	spin_lock(&sch->lock);
+	spin_lock_irqsave(&sch->lock, flags);
 	sch_gpio_reg_set(gc, gpio_num, GLV, val);
-	spin_unlock(&sch->lock);
+	spin_unlock_irqrestore(&sch->lock, flags);
 }
 
 static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
 				  int val)
 {
 	struct sch_gpio *sch = to_sch_gpio(gc);
+	unsigned long flags;
 
-	spin_lock(&sch->lock);
+	spin_lock_irqsave(&sch->lock, flags);
 	sch_gpio_register_clear(sch, gpio_num, GIO);
-	spin_unlock(&sch->lock);
+	spin_unlock_irqrestore(&sch->lock, flags);
 
 	/*
 	 * according to the datasheet, writing to the level register has no
@@ -177,6 +195,13 @@  static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
 	return 0;
 }
 
+static int sch_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+	struct sch_gpio *sch = to_sch_gpio(gc);
+
+	return sch->irq_base + offset;
+}
+
 static struct gpio_chip sch_gpio_chip = {
 	.label			= "sch_gpio",
 	.owner			= THIS_MODULE,
@@ -184,12 +209,160 @@  static struct gpio_chip sch_gpio_chip = {
 	.get			= sch_gpio_get,
 	.direction_output	= sch_gpio_direction_out,
 	.set			= sch_gpio_set,
+	.to_irq			= sch_gpio_to_irq,
+};
+
+static void sch_gpio_irq_enable(struct irq_data *d)
+{
+	struct sch_gpio *sch = container_of(d, struct sch_gpio, data);
+	u32 gpio_num;
+
+	gpio_num = d->irq - sch->irq_base;
+	sch_gpio_register_set(sch, gpio_num, GGPE);
+}
+
+static void sch_gpio_irq_disable(struct irq_data *d)
+{
+	struct sch_gpio *sch = container_of(d, struct sch_gpio, data);
+	u32 gpio_num;
+
+	gpio_num = d->irq - sch->irq_base;
+	sch_gpio_register_clear(sch, gpio_num, GGPE);
+}
+
+static void sch_gpio_irq_ack(struct irq_data *d)
+{
+	struct sch_gpio *sch = container_of(d, struct sch_gpio, data);
+	u32 gpio_num;
+
+	gpio_num = d->irq - sch->irq_base;
+	sch_gpio_reg_set(&(sch->chip), gpio_num, GTS, 1);
+}
+
+static int sch_gpio_irq_type(struct irq_data *d, unsigned type)
+{
+	struct sch_gpio *sch = container_of(d, struct sch_gpio, data);
+	unsigned long flags;
+	u32 gpio_num;
+
+	if (d == NULL)
+		return -EINVAL;
+
+	gpio_num = d->irq - sch->irq_base;
+
+	spin_lock_irqsave(&sch->lock, flags);
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		sch_gpio_register_set(sch, gpio_num, GTPE);
+		sch_gpio_register_clear(sch, gpio_num, GTNE);
+		break;
+
+	case IRQ_TYPE_EDGE_FALLING:
+		sch_gpio_register_set(sch, gpio_num, GTNE);
+		sch_gpio_register_clear(sch, gpio_num, GTPE);
+		break;
+
+	case IRQ_TYPE_EDGE_BOTH:
+		sch_gpio_register_set(sch, gpio_num, GTPE);
+		sch_gpio_register_set(sch, gpio_num, GTNE);
+		break;
+
+	case IRQ_TYPE_NONE:
+		sch_gpio_register_clear(sch, gpio_num, GTPE);
+		sch_gpio_register_clear(sch, gpio_num, GTNE);
+		break;
+
+	default:
+		spin_unlock_irqrestore(&sch->lock, flags);
+		return -EINVAL;
+	}
+
+	spin_unlock_irqrestore(&sch->lock, flags);
+
+	return 0;
+}
+
+static struct irq_chip sch_irq_chip = {
+	.irq_enable	= sch_gpio_irq_enable,
+	.irq_disable	= sch_gpio_irq_disable,
+	.irq_ack	= sch_gpio_irq_ack,
+	.irq_set_type	= sch_gpio_irq_type,
 };
 
+static void sch_gpio_irqs_init(struct sch_gpio *sch, unsigned int num)
+{
+	unsigned int i;
+
+	for (i = 0; i < num; i++) {
+		irq_set_chip_data(i + sch->irq_base, sch);
+		irq_set_chip_and_handler_name(i + sch->irq_base,
+					      &sch_irq_chip,
+					      handle_simple_irq,
+					      "sch_gpio_irq_chip");
+	}
+}
+
+static void sch_gpio_irqs_deinit(struct sch_gpio *sch, unsigned int num)
+{
+	unsigned int i;
+
+	for (i = 0; i < num; i++) {
+		irq_set_chip_data(i + sch->irq_base, 0);
+		irq_set_chip_and_handler_name(i + sch->irq_base, 0, 0, 0);
+	}
+}
+
+static void sch_gpio_irq_disable_all(struct sch_gpio *sch, unsigned int num)
+{
+	unsigned long flags;
+	unsigned int gpio_num;
+
+	spin_lock_irqsave(&sch->lock, flags);
+
+	for (gpio_num = 0; gpio_num < num; gpio_num++) {
+		sch_gpio_register_clear(sch, gpio_num, GTPE);
+		sch_gpio_register_clear(sch, gpio_num, GTNE);
+		sch_gpio_register_clear(sch, gpio_num, GGPE);
+		sch_gpio_register_clear(sch, gpio_num, GSMI);
+
+		if (gpio_num >= 2)
+			sch_gpio_register_clear(sch, gpio_num, RGNMIEN);
+		else
+			sch_gpio_register_clear(sch, gpio_num, CGNMIEN);
+
+		/* clear any pending interrupts */
+		sch_gpio_reg_set(&sch->chip, gpio_num, GTS, 1);
+	}
+
+	spin_unlock_irqrestore(&sch->lock, flags);
+}
+
+static irqreturn_t sch_gpio_irq_handler(int irq, void *dev_id)
+{
+	struct sch_gpio *sch = dev_id;
+	int res;
+	unsigned int i;
+	int ret = IRQ_NONE;
+
+	for (i = 0; i < sch->chip.ngpio; i++) {
+		res = sch_gpio_reg_get(&sch->chip, i, GTS);
+		if (res) {
+			/* clear by setting GTS to 1 */
+			sch_gpio_reg_set(&sch->chip, i, GTS, 1);
+			generic_handle_irq(sch->irq_base + i);
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	return ret;
+}
+
 static int sch_gpio_probe(struct platform_device *pdev)
 {
 	struct sch_gpio *sch;
-	struct resource *res;
+	struct resource *res, *irq;
+	int err;
 
 	sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL);
 	if (!sch)
@@ -203,6 +376,17 @@  static int sch_gpio_probe(struct platform_device *pdev)
 				 pdev->name))
 		return -EBUSY;
 
+	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	sch->irq_support = !!irq;
+	if (sch->irq_support) {
+		sch->irq_num = irq->start;
+		if (sch->irq_num < 0) {
+			dev_warn(&pdev->dev,
+				 "failed to obtain irq number for device\n");
+			sch->irq_support = 0;
+		}
+	}
+
 	spin_lock_init(&sch->lock);
 	sch->iobase = res->start;
 	sch->chip = sch_gpio_chip;
@@ -251,17 +435,72 @@  static int sch_gpio_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	err = gpiochip_add(&sch->chip);
+	if (err < 0)
+		goto err_sch_gpio;
+
+	if (sch->irq_support) {
+		sch->irq_base = irq_alloc_descs(-1, 0, sch->chip.ngpio,
+						NUMA_NO_NODE);
+		if (sch->irq_base < 0) {
+			dev_err(&pdev->dev,
+				"failed to add GPIO IRQ descs\n");
+			sch->irq_base = -1;
+			goto err_sch_intr_chip;
+		}
+
+		/* disable interrupts */
+		sch_gpio_irq_disable_all(sch, sch->chip.ngpio);
+
+		err = request_irq(sch->irq_num, sch_gpio_irq_handler,
+				  IRQF_SHARED, KBUILD_MODNAME, sch);
+		if (err) {
+			dev_err(&pdev->dev,
+				"%s failed to request IRQ\n", __func__);
+			goto err_sch_request_irq;
+		}
+
+		sch_gpio_irqs_init(sch, sch->chip.ngpio);
+	}
+
 	platform_set_drvdata(pdev, sch);
 
-	return gpiochip_add(&sch->chip);
+	return 0;
+
+err_sch_request_irq:
+	irq_free_descs(sch->irq_base, sch->chip.ngpio);
+
+err_sch_intr_chip:
+	if (gpiochip_remove(&sch->chip))
+		dev_err(&pdev->dev,
+			"%s gpiochip_remove() failed\n", __func__);
+
+err_sch_gpio:
+	release_region(res->start, resource_size(res));
+
+	return err;
 }
 
 static int sch_gpio_remove(struct platform_device *pdev)
 {
 	struct sch_gpio *sch = platform_get_drvdata(pdev);
+	int err;
 
-	gpiochip_remove(&sch->chip);
-	return 0;
+	if (sch->irq_support) {
+		sch_gpio_irqs_deinit(sch, sch->chip.ngpio);
+
+		if (sch->irq_num >= 0)
+			free_irq(sch->irq_num, sch);
+
+		irq_free_descs(sch->irq_base, sch->chip.ngpio);
+	}
+
+	err = gpiochip_remove(&sch->chip);
+	if (err)
+		dev_err(&pdev->dev,
+			"%s gpiochip_remove() failed\n", __func__);
+
+	return err;
 }
 
 static struct platform_driver sch_gpio_driver = {