Message ID | 1411726047-3224-4-git-send-email-rebecca.swee.fun.chang@intel.com |
---|---|
State | Not Applicable, archived |
Headers | show |
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
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 --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 = {
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(-)