Merge branch 'work.splice_read' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / arch / arm / common / sa1111.c
index 2e076c4..4ecd512 100644 (file)
@@ -15,6 +15,7 @@
  * from machine specific code with proper arguments when required.
  */
 #include <linux/module.h>
+#include <linux/gpio/driver.h>
 #include <linux/init.h>
 #include <linux/irq.h>
 #include <linux/kernel.h>
@@ -107,6 +108,7 @@ struct sa1111 {
        spinlock_t      lock;
        void __iomem    *base;
        struct sa1111_platform_data *pdata;
+       struct gpio_chip gc;
 #ifdef CONFIG_PM
        void            *saved_state;
 #endif
@@ -231,132 +233,44 @@ static void sa1111_irq_handler(struct irq_desc *desc)
 #define SA1111_IRQMASK_LO(x)   (1 << (x - sachip->irq_base))
 #define SA1111_IRQMASK_HI(x)   (1 << (x - sachip->irq_base - 32))
 
-static void sa1111_ack_irq(struct irq_data *d)
-{
-}
-
-static void sa1111_mask_lowirq(struct irq_data *d)
+static u32 sa1111_irqmask(struct irq_data *d)
 {
        struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
-       void __iomem *mapbase = sachip->base + SA1111_INTC;
-       unsigned long ie0;
 
-       ie0 = sa1111_readl(mapbase + SA1111_INTEN0);
-       ie0 &= ~SA1111_IRQMASK_LO(d->irq);
-       writel(ie0, mapbase + SA1111_INTEN0);
+       return BIT((d->irq - sachip->irq_base) & 31);
 }
 
-static void sa1111_unmask_lowirq(struct irq_data *d)
+static int sa1111_irqbank(struct irq_data *d)
 {
        struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
-       void __iomem *mapbase = sachip->base + SA1111_INTC;
-       unsigned long ie0;
-
-       ie0 = sa1111_readl(mapbase + SA1111_INTEN0);
-       ie0 |= SA1111_IRQMASK_LO(d->irq);
-       sa1111_writel(ie0, mapbase + SA1111_INTEN0);
-}
-
-/*
- * Attempt to re-trigger the interrupt.  The SA1111 contains a register
- * (INTSET) which claims to do this.  However, in practice no amount of
- * manipulation of INTEN and INTSET guarantees that the interrupt will
- * be triggered.  In fact, its very difficult, if not impossible to get
- * INTSET to re-trigger the interrupt.
- */
-static int sa1111_retrigger_lowirq(struct irq_data *d)
-{
-       struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
-       void __iomem *mapbase = sachip->base + SA1111_INTC;
-       unsigned int mask = SA1111_IRQMASK_LO(d->irq);
-       unsigned long ip0;
-       int i;
-
-       ip0 = sa1111_readl(mapbase + SA1111_INTPOL0);
-       for (i = 0; i < 8; i++) {
-               sa1111_writel(ip0 ^ mask, mapbase + SA1111_INTPOL0);
-               sa1111_writel(ip0, mapbase + SA1111_INTPOL0);
-               if (sa1111_readl(mapbase + SA1111_INTSTATCLR0) & mask)
-                       break;
-       }
 
-       if (i == 8)
-               pr_err("Danger Will Robinson: failed to re-trigger IRQ%d\n",
-                      d->irq);
-       return i == 8 ? -1 : 0;
+       return ((d->irq - sachip->irq_base) / 32) * 4;
 }
 
-static int sa1111_type_lowirq(struct irq_data *d, unsigned int flags)
-{
-       struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
-       void __iomem *mapbase = sachip->base + SA1111_INTC;
-       unsigned int mask = SA1111_IRQMASK_LO(d->irq);
-       unsigned long ip0;
-
-       if (flags == IRQ_TYPE_PROBE)
-               return 0;
-
-       if ((!(flags & IRQ_TYPE_EDGE_RISING) ^ !(flags & IRQ_TYPE_EDGE_FALLING)) == 0)
-               return -EINVAL;
-
-       ip0 = sa1111_readl(mapbase + SA1111_INTPOL0);
-       if (flags & IRQ_TYPE_EDGE_RISING)
-               ip0 &= ~mask;
-       else
-               ip0 |= mask;
-       sa1111_writel(ip0, mapbase + SA1111_INTPOL0);
-       sa1111_writel(ip0, mapbase + SA1111_WAKEPOL0);
-
-       return 0;
-}
-
-static int sa1111_wake_lowirq(struct irq_data *d, unsigned int on)
+static void sa1111_ack_irq(struct irq_data *d)
 {
-       struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
-       void __iomem *mapbase = sachip->base + SA1111_INTC;
-       unsigned int mask = SA1111_IRQMASK_LO(d->irq);
-       unsigned long we0;
-
-       we0 = sa1111_readl(mapbase + SA1111_WAKEEN0);
-       if (on)
-               we0 |= mask;
-       else
-               we0 &= ~mask;
-       sa1111_writel(we0, mapbase + SA1111_WAKEEN0);
-
-       return 0;
 }
 
-static struct irq_chip sa1111_low_chip = {
-       .name           = "SA1111-l",
-       .irq_ack        = sa1111_ack_irq,
-       .irq_mask       = sa1111_mask_lowirq,
-       .irq_unmask     = sa1111_unmask_lowirq,
-       .irq_retrigger  = sa1111_retrigger_lowirq,
-       .irq_set_type   = sa1111_type_lowirq,
-       .irq_set_wake   = sa1111_wake_lowirq,
-};
-
-static void sa1111_mask_highirq(struct irq_data *d)
+static void sa1111_mask_irq(struct irq_data *d)
 {
        struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
-       void __iomem *mapbase = sachip->base + SA1111_INTC;
-       unsigned long ie1;
+       void __iomem *mapbase = sachip->base + SA1111_INTC + sa1111_irqbank(d);
+       u32 ie;
 
-       ie1 = sa1111_readl(mapbase + SA1111_INTEN1);
-       ie1 &= ~SA1111_IRQMASK_HI(d->irq);
-       sa1111_writel(ie1, mapbase + SA1111_INTEN1);
+       ie = sa1111_readl(mapbase + SA1111_INTEN0);
+       ie &= ~sa1111_irqmask(d);
+       sa1111_writel(ie, mapbase + SA1111_INTEN0);
 }
 
-static void sa1111_unmask_highirq(struct irq_data *d)
+static void sa1111_unmask_irq(struct irq_data *d)
 {
        struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
-       void __iomem *mapbase = sachip->base + SA1111_INTC;
-       unsigned long ie1;
+       void __iomem *mapbase = sachip->base + SA1111_INTC + sa1111_irqbank(d);
+       u32 ie;
 
-       ie1 = sa1111_readl(mapbase + SA1111_INTEN1);
-       ie1 |= SA1111_IRQMASK_HI(d->irq);
-       sa1111_writel(ie1, mapbase + SA1111_INTEN1);
+       ie = sa1111_readl(mapbase + SA1111_INTEN0);
+       ie |= sa1111_irqmask(d);
+       sa1111_writel(ie, mapbase + SA1111_INTEN0);
 }
 
 /*
@@ -366,19 +280,18 @@ static void sa1111_unmask_highirq(struct irq_data *d)
  * be triggered.  In fact, its very difficult, if not impossible to get
  * INTSET to re-trigger the interrupt.
  */
-static int sa1111_retrigger_highirq(struct irq_data *d)
+static int sa1111_retrigger_irq(struct irq_data *d)
 {
        struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
-       void __iomem *mapbase = sachip->base + SA1111_INTC;
-       unsigned int mask = SA1111_IRQMASK_HI(d->irq);
-       unsigned long ip1;
+       void __iomem *mapbase = sachip->base + SA1111_INTC + sa1111_irqbank(d);
+       u32 ip, mask = sa1111_irqmask(d);
        int i;
 
-       ip1 = sa1111_readl(mapbase + SA1111_INTPOL1);
+       ip = sa1111_readl(mapbase + SA1111_INTPOL0);
        for (i = 0; i < 8; i++) {
-               sa1111_writel(ip1 ^ mask, mapbase + SA1111_INTPOL1);
-               sa1111_writel(ip1, mapbase + SA1111_INTPOL1);
-               if (sa1111_readl(mapbase + SA1111_INTSTATCLR1) & mask)
+               sa1111_writel(ip ^ mask, mapbase + SA1111_INTPOL0);
+               sa1111_writel(ip, mapbase + SA1111_INTPOL0);
+               if (sa1111_readl(mapbase + SA1111_INTSTATCLR0) & mask)
                        break;
        }
 
@@ -388,12 +301,11 @@ static int sa1111_retrigger_highirq(struct irq_data *d)
        return i == 8 ? -1 : 0;
 }
 
-static int sa1111_type_highirq(struct irq_data *d, unsigned int flags)
+static int sa1111_type_irq(struct irq_data *d, unsigned int flags)
 {
        struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
-       void __iomem *mapbase = sachip->base + SA1111_INTC;
-       unsigned int mask = SA1111_IRQMASK_HI(d->irq);
-       unsigned long ip1;
+       void __iomem *mapbase = sachip->base + SA1111_INTC + sa1111_irqbank(d);
+       u32 ip, mask = sa1111_irqmask(d);
 
        if (flags == IRQ_TYPE_PROBE)
                return 0;
@@ -401,42 +313,41 @@ static int sa1111_type_highirq(struct irq_data *d, unsigned int flags)
        if ((!(flags & IRQ_TYPE_EDGE_RISING) ^ !(flags & IRQ_TYPE_EDGE_FALLING)) == 0)
                return -EINVAL;
 
-       ip1 = sa1111_readl(mapbase + SA1111_INTPOL1);
+       ip = sa1111_readl(mapbase + SA1111_INTPOL0);
        if (flags & IRQ_TYPE_EDGE_RISING)
-               ip1 &= ~mask;
+               ip &= ~mask;
        else
-               ip1 |= mask;
-       sa1111_writel(ip1, mapbase + SA1111_INTPOL1);
-       sa1111_writel(ip1, mapbase + SA1111_WAKEPOL1);
+               ip |= mask;
+       sa1111_writel(ip, mapbase + SA1111_INTPOL0);
+       sa1111_writel(ip, mapbase + SA1111_WAKEPOL0);
 
        return 0;
 }
 
-static int sa1111_wake_highirq(struct irq_data *d, unsigned int on)
+static int sa1111_wake_irq(struct irq_data *d, unsigned int on)
 {
        struct sa1111 *sachip = irq_data_get_irq_chip_data(d);
-       void __iomem *mapbase = sachip->base + SA1111_INTC;
-       unsigned int mask = SA1111_IRQMASK_HI(d->irq);
-       unsigned long we1;
+       void __iomem *mapbase = sachip->base + SA1111_INTC + sa1111_irqbank(d);
+       u32 we, mask = sa1111_irqmask(d);
 
-       we1 = sa1111_readl(mapbase + SA1111_WAKEEN1);
+       we = sa1111_readl(mapbase + SA1111_WAKEEN0);
        if (on)
-               we1 |= mask;
+               we |= mask;
        else
-               we1 &= ~mask;
-       sa1111_writel(we1, mapbase + SA1111_WAKEEN1);
+               we &= ~mask;
+       sa1111_writel(we, mapbase + SA1111_WAKEEN0);
 
        return 0;
 }
 
-static struct irq_chip sa1111_high_chip = {
-       .name           = "SA1111-h",
+static struct irq_chip sa1111_irq_chip = {
+       .name           = "SA1111",
        .irq_ack        = sa1111_ack_irq,
-       .irq_mask       = sa1111_mask_highirq,
-       .irq_unmask     = sa1111_unmask_highirq,
-       .irq_retrigger  = sa1111_retrigger_highirq,
-       .irq_set_type   = sa1111_type_highirq,
-       .irq_set_wake   = sa1111_wake_highirq,
+       .irq_mask       = sa1111_mask_irq,
+       .irq_unmask     = sa1111_unmask_irq,
+       .irq_retrigger  = sa1111_retrigger_irq,
+       .irq_set_type   = sa1111_type_irq,
+       .irq_set_wake   = sa1111_wake_irq,
 };
 
 static int sa1111_setup_irq(struct sa1111 *sachip, unsigned irq_base)
@@ -482,16 +393,14 @@ static int sa1111_setup_irq(struct sa1111 *sachip, unsigned irq_base)
 
        for (i = IRQ_GPAIN0; i <= SSPROR; i++) {
                irq = sachip->irq_base + i;
-               irq_set_chip_and_handler(irq, &sa1111_low_chip,
-                                        handle_edge_irq);
+               irq_set_chip_and_handler(irq, &sa1111_irq_chip, handle_edge_irq);
                irq_set_chip_data(irq, sachip);
                irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
        }
 
        for (i = AUDXMTDMADONEA; i <= IRQ_S1_BVD1_STSCHG; i++) {
                irq = sachip->irq_base + i;
-               irq_set_chip_and_handler(irq, &sa1111_high_chip,
-                                        handle_edge_irq);
+               irq_set_chip_and_handler(irq, &sa1111_irq_chip, handle_edge_irq);
                irq_set_chip_data(irq, sachip);
                irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
        }
@@ -509,6 +418,181 @@ static int sa1111_setup_irq(struct sa1111 *sachip, unsigned irq_base)
        return 0;
 }
 
+static void sa1111_remove_irq(struct sa1111 *sachip)
+{
+       void __iomem *irqbase = sachip->base + SA1111_INTC;
+
+       /* disable all IRQs */
+       sa1111_writel(0, irqbase + SA1111_INTEN0);
+       sa1111_writel(0, irqbase + SA1111_INTEN1);
+       sa1111_writel(0, irqbase + SA1111_WAKEEN0);
+       sa1111_writel(0, irqbase + SA1111_WAKEEN1);
+
+       if (sachip->irq != NO_IRQ) {
+               irq_set_chained_handler_and_data(sachip->irq, NULL, NULL);
+               irq_free_descs(sachip->irq_base, SA1111_IRQ_NR);
+
+               release_mem_region(sachip->phys + SA1111_INTC, 512);
+       }
+}
+
+enum {
+       SA1111_GPIO_PXDDR = (SA1111_GPIO_PADDR - SA1111_GPIO_PADDR),
+       SA1111_GPIO_PXDRR = (SA1111_GPIO_PADRR - SA1111_GPIO_PADDR),
+       SA1111_GPIO_PXDWR = (SA1111_GPIO_PADWR - SA1111_GPIO_PADDR),
+       SA1111_GPIO_PXSDR = (SA1111_GPIO_PASDR - SA1111_GPIO_PADDR),
+       SA1111_GPIO_PXSSR = (SA1111_GPIO_PASSR - SA1111_GPIO_PADDR),
+};
+
+static struct sa1111 *gc_to_sa1111(struct gpio_chip *gc)
+{
+       return container_of(gc, struct sa1111, gc);
+}
+
+static void __iomem *sa1111_gpio_map_reg(struct sa1111 *sachip, unsigned offset)
+{
+       void __iomem *reg = sachip->base + SA1111_GPIO;
+
+       if (offset < 4)
+               return reg + SA1111_GPIO_PADDR;
+       if (offset < 10)
+               return reg + SA1111_GPIO_PBDDR;
+       if (offset < 18)
+               return reg + SA1111_GPIO_PCDDR;
+       return NULL;
+}
+
+static u32 sa1111_gpio_map_bit(unsigned offset)
+{
+       if (offset < 4)
+               return BIT(offset);
+       if (offset < 10)
+               return BIT(offset - 4);
+       if (offset < 18)
+               return BIT(offset - 10);
+       return 0;
+}
+
+static void sa1111_gpio_modify(void __iomem *reg, u32 mask, u32 set)
+{
+       u32 val;
+
+       val = readl_relaxed(reg);
+       val &= ~mask;
+       val |= mask & set;
+       writel_relaxed(val, reg);
+}
+
+static int sa1111_gpio_get_direction(struct gpio_chip *gc, unsigned offset)
+{
+       struct sa1111 *sachip = gc_to_sa1111(gc);
+       void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
+       u32 mask = sa1111_gpio_map_bit(offset);
+
+       return !!(readl_relaxed(reg + SA1111_GPIO_PXDDR) & mask);
+}
+
+static int sa1111_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+       struct sa1111 *sachip = gc_to_sa1111(gc);
+       unsigned long flags;
+       void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
+       u32 mask = sa1111_gpio_map_bit(offset);
+
+       spin_lock_irqsave(&sachip->lock, flags);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PXDDR, mask, mask);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PXSDR, mask, mask);
+       spin_unlock_irqrestore(&sachip->lock, flags);
+
+       return 0;
+}
+
+static int sa1111_gpio_direction_output(struct gpio_chip *gc, unsigned offset,
+       int value)
+{
+       struct sa1111 *sachip = gc_to_sa1111(gc);
+       unsigned long flags;
+       void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
+       u32 mask = sa1111_gpio_map_bit(offset);
+
+       spin_lock_irqsave(&sachip->lock, flags);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PXDWR, mask, value ? mask : 0);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PXSSR, mask, value ? mask : 0);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PXDDR, mask, 0);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PXSDR, mask, 0);
+       spin_unlock_irqrestore(&sachip->lock, flags);
+
+       return 0;
+}
+
+static int sa1111_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+       struct sa1111 *sachip = gc_to_sa1111(gc);
+       void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
+       u32 mask = sa1111_gpio_map_bit(offset);
+
+       return !!(readl_relaxed(reg + SA1111_GPIO_PXDRR) & mask);
+}
+
+static void sa1111_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+       struct sa1111 *sachip = gc_to_sa1111(gc);
+       unsigned long flags;
+       void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
+       u32 mask = sa1111_gpio_map_bit(offset);
+
+       spin_lock_irqsave(&sachip->lock, flags);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PXDWR, mask, value ? mask : 0);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PXSSR, mask, value ? mask : 0);
+       spin_unlock_irqrestore(&sachip->lock, flags);
+}
+
+static void sa1111_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
+       unsigned long *bits)
+{
+       struct sa1111 *sachip = gc_to_sa1111(gc);
+       unsigned long flags;
+       void __iomem *reg = sachip->base + SA1111_GPIO;
+       u32 msk, val;
+
+       msk = *mask;
+       val = *bits;
+
+       spin_lock_irqsave(&sachip->lock, flags);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PADWR, msk & 15, val);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PASSR, msk & 15, val);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PBDWR, (msk >> 4) & 255, val >> 4);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PBSSR, (msk >> 4) & 255, val >> 4);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PCDWR, (msk >> 12) & 255, val >> 12);
+       sa1111_gpio_modify(reg + SA1111_GPIO_PCSSR, (msk >> 12) & 255, val >> 12);
+       spin_unlock_irqrestore(&sachip->lock, flags);
+}
+
+static int sa1111_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+       struct sa1111 *sachip = gc_to_sa1111(gc);
+
+       return sachip->irq_base + offset;
+}
+
+static int sa1111_setup_gpios(struct sa1111 *sachip)
+{
+       sachip->gc.label = "sa1111";
+       sachip->gc.parent = sachip->dev;
+       sachip->gc.owner = THIS_MODULE;
+       sachip->gc.get_direction = sa1111_gpio_get_direction;
+       sachip->gc.direction_input = sa1111_gpio_direction_input;
+       sachip->gc.direction_output = sa1111_gpio_direction_output;
+       sachip->gc.get = sa1111_gpio_get;
+       sachip->gc.set = sa1111_gpio_set;
+       sachip->gc.set_multiple = sa1111_gpio_set_multiple;
+       sachip->gc.to_irq = sa1111_gpio_to_irq;
+       sachip->gc.base = -1;
+       sachip->gc.ngpio = 18;
+
+       return devm_gpiochip_add_data(sachip->dev, &sachip->gc, sachip);
+}
+
 /*
  * Bring the SA1111 out of reset.  This requires a set procedure:
  *  1. nRESET asserted (by hardware)
@@ -607,7 +691,7 @@ sa1111_configure_smc(struct sa1111 *sachip, int sdram, unsigned int drac,
 
 static void sa1111_dev_release(struct device *_dev)
 {
-       struct sa1111_dev *dev = SA1111_DEV(_dev);
+       struct sa1111_dev *dev = to_sa1111_device(_dev);
 
        kfree(dev);
 }
@@ -696,19 +780,17 @@ static int __sa1111_probe(struct device *me, struct resource *mem, int irq)
        if (!pd)
                return -EINVAL;
 
-       sachip = kzalloc(sizeof(struct sa1111), GFP_KERNEL);
+       sachip = devm_kzalloc(me, sizeof(struct sa1111), GFP_KERNEL);
        if (!sachip)
                return -ENOMEM;
 
-       sachip->clk = clk_get(me, "SA1111_CLK");
-       if (IS_ERR(sachip->clk)) {
-               ret = PTR_ERR(sachip->clk);
-               goto err_free;
-       }
+       sachip->clk = devm_clk_get(me, "SA1111_CLK");
+       if (IS_ERR(sachip->clk))
+               return PTR_ERR(sachip->clk);
 
        ret = clk_prepare(sachip->clk);
        if (ret)
-               goto err_clkput;
+               return ret;
 
        spin_lock_init(&sachip->lock);
 
@@ -757,6 +839,11 @@ static int __sa1111_probe(struct device *me, struct resource *mem, int irq)
                        goto err_clk;
        }
 
+       /* Setup the GPIOs - should really be done after the IRQ setup */
+       ret = sa1111_setup_gpios(sachip);
+       if (ret)
+               goto err_irq;
+
 #ifdef CONFIG_ARCH_SA1100
        {
        unsigned int val;
@@ -799,22 +886,22 @@ static int __sa1111_probe(struct device *me, struct resource *mem, int irq)
 
        return 0;
 
+ err_irq:
+       sa1111_remove_irq(sachip);
  err_clk:
        clk_disable(sachip->clk);
  err_unmap:
        iounmap(sachip->base);
  err_clk_unprep:
        clk_unprepare(sachip->clk);
- err_clkput:
-       clk_put(sachip->clk);
- err_free:
-       kfree(sachip);
        return ret;
 }
 
 static int sa1111_remove_one(struct device *dev, void *data)
 {
-       struct sa1111_dev *sadev = SA1111_DEV(dev);
+       struct sa1111_dev *sadev = to_sa1111_device(dev);
+       if (dev->bus != &sa1111_bus_type)
+               return 0;
        device_del(&sadev->dev);
        release_resource(&sadev->res);
        put_device(&sadev->dev);
@@ -823,29 +910,14 @@ static int sa1111_remove_one(struct device *dev, void *data)
 
 static void __sa1111_remove(struct sa1111 *sachip)
 {
-       void __iomem *irqbase = sachip->base + SA1111_INTC;
-
        device_for_each_child(sachip->dev, NULL, sa1111_remove_one);
 
-       /* disable all IRQs */
-       sa1111_writel(0, irqbase + SA1111_INTEN0);
-       sa1111_writel(0, irqbase + SA1111_INTEN1);
-       sa1111_writel(0, irqbase + SA1111_WAKEEN0);
-       sa1111_writel(0, irqbase + SA1111_WAKEEN1);
+       sa1111_remove_irq(sachip);
 
        clk_disable(sachip->clk);
        clk_unprepare(sachip->clk);
 
-       if (sachip->irq != NO_IRQ) {
-               irq_set_chained_handler_and_data(sachip->irq, NULL, NULL);
-               irq_free_descs(sachip->irq_base, SA1111_IRQ_NR);
-
-               release_mem_region(sachip->phys + SA1111_INTC, 512);
-       }
-
        iounmap(sachip->base);
-       clk_put(sachip->clk);
-       kfree(sachip);
 }
 
 struct sa1111_save_data {
@@ -1285,6 +1357,14 @@ void sa1111_disable_device(struct sa1111_dev *sadev)
 }
 EXPORT_SYMBOL(sa1111_disable_device);
 
+int sa1111_get_irq(struct sa1111_dev *sadev, unsigned num)
+{
+       if (num >= ARRAY_SIZE(sadev->irq))
+               return -EINVAL;
+       return sadev->irq[num];
+}
+EXPORT_SYMBOL_GPL(sa1111_get_irq);
+
 /*
  *     SA1111 "Register Access Bus."
  *
@@ -1293,7 +1373,7 @@ EXPORT_SYMBOL(sa1111_disable_device);
  */
 static int sa1111_match(struct device *_dev, struct device_driver *_drv)
 {
-       struct sa1111_dev *dev = SA1111_DEV(_dev);
+       struct sa1111_dev *dev = to_sa1111_device(_dev);
        struct sa1111_driver *drv = SA1111_DRV(_drv);
 
        return !!(dev->devid & drv->devid);
@@ -1301,7 +1381,7 @@ static int sa1111_match(struct device *_dev, struct device_driver *_drv)
 
 static int sa1111_bus_suspend(struct device *dev, pm_message_t state)
 {
-       struct sa1111_dev *sadev = SA1111_DEV(dev);
+       struct sa1111_dev *sadev = to_sa1111_device(dev);
        struct sa1111_driver *drv = SA1111_DRV(dev->driver);
        int ret = 0;
 
@@ -1312,7 +1392,7 @@ static int sa1111_bus_suspend(struct device *dev, pm_message_t state)
 
 static int sa1111_bus_resume(struct device *dev)
 {
-       struct sa1111_dev *sadev = SA1111_DEV(dev);
+       struct sa1111_dev *sadev = to_sa1111_device(dev);
        struct sa1111_driver *drv = SA1111_DRV(dev->driver);
        int ret = 0;
 
@@ -1326,12 +1406,12 @@ static void sa1111_bus_shutdown(struct device *dev)
        struct sa1111_driver *drv = SA1111_DRV(dev->driver);
 
        if (drv && drv->shutdown)
-               drv->shutdown(SA1111_DEV(dev));
+               drv->shutdown(to_sa1111_device(dev));
 }
 
 static int sa1111_bus_probe(struct device *dev)
 {
-       struct sa1111_dev *sadev = SA1111_DEV(dev);
+       struct sa1111_dev *sadev = to_sa1111_device(dev);
        struct sa1111_driver *drv = SA1111_DRV(dev->driver);
        int ret = -ENODEV;
 
@@ -1342,7 +1422,7 @@ static int sa1111_bus_probe(struct device *dev)
 
 static int sa1111_bus_remove(struct device *dev)
 {
-       struct sa1111_dev *sadev = SA1111_DEV(dev);
+       struct sa1111_dev *sadev = to_sa1111_device(dev);
        struct sa1111_driver *drv = SA1111_DRV(dev->driver);
        int ret = 0;
 
@@ -1407,7 +1487,7 @@ static int sa1111_needs_bounce(struct device *dev, dma_addr_t addr, size_t size)
 static int sa1111_notifier_call(struct notifier_block *n, unsigned long action,
        void *data)
 {
-       struct sa1111_dev *dev = SA1111_DEV(data);
+       struct sa1111_dev *dev = to_sa1111_device(data);
 
        switch (action) {
        case BUS_NOTIFY_ADD_DEVICE: