S3C2443 CPU has a problem with incorrect reading from EXTINTn
registers. So s3c_irqext_type function wrongly modifies them.
So add special function for s3c2443, to handle this case.

Signed-off-by: Alexander Varnin <fenix...@mail.ru>
---
 arch/arm/mach-s3c24xx/s3c2443.c |    8 ++++
 arch/arm/plat-s3c24xx/irq.c     |   89 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c24xx/s3c2443.c b/arch/arm/mach-s3c24xx/s3c2443.c
index ab648ad..99eef31 100644
--- a/arch/arm/mach-s3c24xx/s3c2443.c
+++ b/arch/arm/mach-s3c24xx/s3c2443.c
@@ -67,8 +67,11 @@ void s3c2443_restart(char mode, const char *cmd)
        __raw_writel(S3C2443_SWRST_RESET, S3C2443_SWRST);
 }
 
+extern int s3c2443_irqext_type(struct irq_data *data, unsigned int type);
+
 int __init s3c2443_init(void)
 {
+       struct irq_chip * chip;
        printk("S3C2443: Initialising architecture\n");
 
        s3c_nand_setname("s3c2412-nand");
@@ -81,6 +84,11 @@ int __init s3c2443_init(void)
        s3c_device_wdt.resource[1].start = IRQ_S3C2443_WDT;
        s3c_device_wdt.resource[1].end   = IRQ_S3C2443_WDT;
 
+       chip = irq_get_chip(IRQ_EINT0);
+       chip->irq_set_type=s3c2443_irqext_type;
+       chip = irq_get_chip(IRQ_EINT4);
+       chip->irq_set_type=s3c2443_irqext_type;
+
        return device_register(&s3c2443_dev);
 }
 
diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c
index fe57bbb..9c815f3 100644
--- a/arch/arm/plat-s3c24xx/irq.c
+++ b/arch/arm/plat-s3c24xx/irq.c
@@ -224,7 +224,96 @@ s3c_irqext_type(struct irq_data *data, unsigned int type)
 
        return 0;
 }
+#if defined(CONFIG_CPU_S3C2443)
+int
+s3c2443_irqext_type(struct irq_data *data, unsigned int type)
+{
+       int i;
+       int fixed;
+       void __iomem *extint_reg;
+       void __iomem *gpcon_reg;
+       unsigned long gpcon_offset, extint_offset;
+       unsigned long newvalue = 0, value;
+
+       if ((data->irq >= IRQ_EINT0) && (data->irq <= IRQ_EINT3)) {
+               gpcon_reg = S3C2410_GPFCON;
+               extint_reg = S3C24XX_EXTINT0;
+               gpcon_offset = (data->irq - IRQ_EINT0) * 2;
+               extint_offset = (data->irq - IRQ_EINT0) * 4;
+       } else if ((data->irq >= IRQ_EINT4) && (data->irq <= IRQ_EINT7)) {
+               gpcon_reg = S3C2410_GPFCON;
+               extint_reg = S3C24XX_EXTINT0;
+               gpcon_offset = (data->irq - (EXTINT_OFF)) * 2;
+               extint_offset = (data->irq - (EXTINT_OFF)) * 4;
+       } else if ((data->irq >= IRQ_EINT8) && (data->irq <= IRQ_EINT15)) {
+               gpcon_reg = S3C2410_GPGCON;
+               extint_reg = S3C24XX_EXTINT1;
+               gpcon_offset = (data->irq - IRQ_EINT8) * 2;
+               extint_offset = (data->irq - IRQ_EINT8) * 4;
+       } else if ((data->irq >= IRQ_EINT16) && (data->irq <= IRQ_EINT23)) {
+               gpcon_reg = S3C2410_GPGCON;
+               extint_reg = S3C24XX_EXTINT2;
+               gpcon_offset = (data->irq - IRQ_EINT8) * 2;
+               extint_offset = (data->irq - IRQ_EINT16) * 4;
+       } else {
+               return -1;
+       }
 
+       /* Set the GPIO to external interrupt mode */
+       value = __raw_readl(gpcon_reg);
+       value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
+       __raw_writel(value, gpcon_reg);
+
+       /* Set the external interrupt to pointed trigger type */
+       switch (type)
+       {
+               case IRQ_TYPE_NONE:
+                       printk(KERN_WARNING "No edge setting!\n");
+                       break;
+
+               case IRQ_TYPE_EDGE_RISING:
+                       newvalue = S3C2410_EXTINT_RISEEDGE;
+                       break;
+
+               case IRQ_TYPE_EDGE_FALLING:
+                       newvalue = S3C2410_EXTINT_FALLEDGE;
+                       break;
+
+               case IRQ_TYPE_EDGE_BOTH:
+                       newvalue = S3C2410_EXTINT_BOTHEDGE;
+                       break;
+
+               case IRQ_TYPE_LEVEL_LOW:
+                       newvalue = S3C2410_EXTINT_LOWLEV;
+                       break;
+
+               case IRQ_TYPE_LEVEL_HIGH:
+                       newvalue = S3C2410_EXTINT_HILEV;
+                       break;
+
+               default:
+                       printk(KERN_ERR "No such irq type %d", type);
+                       return -1;
+       }
+
+       value = __raw_readl(extint_reg);
+       //Hack for 2443 error workaround
+       fixed = 0;
+       if(extint_reg == S3C24XX_EXTINT1 || extint_reg == S3C24XX_EXTINT2)
+               for(i=0; i<7;i++)
+                       fixed |= (((value >> ((7-i)*4+1)) & 7) | ((value >> 
((7-i)*4-3)) & 8)) << i*4;
+       else
+               for(i=0; i<7;i++)
+                       fixed |= ( (value >> (7-i)*4) & 0xf )  << i*4;
+        fixed |= (((value>>1) & 7) | ((value<<3) & 8)) << 27;
+       value = fixed;
+
+       value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
+       __raw_writel(value, extint_reg);
+
+       return 0;
+}
+#endif //defined(CONFIG_CPU_S3C2443)
 static struct irq_chip s3c_irqext_chip = {
        .name           = "s3c-ext",
        .irq_mask       = s3c_irqext_mask,
-- 
1.7.2.5

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to