Hi a few more comments here: On Fri, 16 Sep 2011, Tero Kristo wrote: > diff --git a/drivers/power/omap_prm.c b/drivers/power/omap_prm.c > index dfc0920..880748a 100644 > --- a/drivers/power/omap_prm.c > +++ b/drivers/power/omap_prm.c > #define DRIVER_NAME "omap-prm" > +#define OMAP_PRCM_MAX_NR_PENDING_REG 2 > + > +struct omap_prcm_irq_setup { > + u32 ack; > + u32 mask; > + int nr_regs; > +}; > > struct omap_prm_device { > - struct platform_device pdev; > + struct platform_device pdev; > + const struct omap_prcm_irq_setup *irq_setup; > + struct irq_chip_generic **irq_chips; > + int suspended; > + u32 *saved_ena; > + u32 *priority_mask; > + int base_irq; > + int irq; > + void __iomem *base; > + int revision; > +}; > + > +#define OMAP3_PRM_REVISION 0x10 > +#define OMAP4_PRM_REVISION 0x40000100 > + > +#define PRM_OMAP3 0x1 > +#define PRM_OMAP4 0x2 These should no longer be needed, as far as I can see. > +#define OMAP3_PRM_IRQSTATUS_OFFSET 0x818 > +#define OMAP3_PRM_IRQENABLE_OFFSET 0x81c > +#define OMAP4_PRM_IRQSTATUS_OFFSET 0x10 > +#define OMAP4_PRM_IRQENABLE_OFFSET 0x18 It probably would be best to put these in header files, and also just to cut and paste the needed macros from the arch/arm/mach-omap2/prm*.h files - the 44xx files are autogenerated and the 24xx/34xx files were partially autogenerated. > + > +static const struct omap_prcm_irq_setup omap3_prcm_irq_setup = { > + .ack = OMAP3_PRM_IRQSTATUS_OFFSET, > + .mask = OMAP3_PRM_IRQENABLE_OFFSET, > + .nr_regs = 1, > +}; > + > +static const struct omap_prcm_irq_setup omap4_prcm_irq_setup = { > + .ack = OMAP4_PRM_IRQSTATUS_OFFSET, > + .mask = OMAP4_PRM_IRQENABLE_OFFSET, > + .nr_regs = 2, > }; > > static struct omap_prm_device prm_dev = { > @@ -33,20 +77,321 @@ static struct omap_prm_device prm_dev = { > }, > }; > > -static int __init omap_prm_probe(struct platform_device *pdev) > +struct omap_prcm_irq { > + const char *name; > + unsigned int offset; > + bool priority; > + u32 supported_rev; > +}; > + > +#define OMAP_PRCM_IRQ(_name, _offset, _high_priority, _rev) { \ > + .name = _name, \ > + .offset = _offset, \ > + .priority = _high_priority, \ > + .supported_rev = _rev \ > + } > + > +static struct omap_prcm_irq omap_prcm_irqs[] = { > + OMAP_PRCM_IRQ("wkup", 0, 0, PRM_OMAP3), > + OMAP_PRCM_IRQ("io", 9, 1, PRM_OMAP3 | PRM_OMAP4), > +}; > + > +static inline u32 prm_read_reg(int offset) > +{ > + return __raw_readl(prm_dev.base + offset); > +} > + > +static inline void prm_write_reg(u32 value, int offset) > +{ > + __raw_writel(value, prm_dev.base + offset); > +} What to do with these functions will depend on how you split the files up. Based on a naïve look, I'd suggest putting copies of prcm_irq_handler() into each of the omap*_prm.c files, calling local static functions for read_pending_events() and save_and_disable_irqenable_bits(), and then calling into common code in omap_prm_common.c for the rest of the function. Looks like omap_prm_complete() would also need PRM-variant-specific copies. I guess most of the rest could be common code? > + > +static void prm_pending_events(unsigned long *events) > +{ > + u32 ena, st; > + int i; > + > + memset(events, 0, prm_dev.irq_setup->nr_regs * 4); > + > + for (i = 0; i < prm_dev.irq_setup->nr_regs; i++) { > + ena = prm_read_reg(prm_dev.irq_setup->mask + i * 4); > + st = prm_read_reg(prm_dev.irq_setup->ack + i * 4); > + events[i] = ena & st; > + } > +} > + > +static void prm_events_filter_priority(unsigned long *events, > + unsigned long *priority_events) > +{ > + int i; > + > + for (i = 0; i < prm_dev.irq_setup->nr_regs; i++) { > + priority_events[i] = events[i] & prm_dev.priority_mask[i]; > + events[i] ^= priority_events[i]; > + } > +} > + > +/* > + * PRCM Interrupt Handler > + * > + * The PRM_IRQSTATUS_MPU register indicates if there are any pending > + * interrupts from the PRCM for the MPU. These bits must be cleared in > + * order to clear the PRCM interrupt. The PRCM interrupt handler is > + * implemented to simply clear the PRM_IRQSTATUS_MPU in order to clear > + * the PRCM interrupt. Please note that bit 0 of the PRM_IRQSTATUS_MPU > + * register indicates that a wake-up event is pending for the MPU and > + * this bit can only be cleared if the all the wake-up events latched > + * in the various PM_WKST_x registers have been cleared. The interrupt > + * handler is implemented using a do-while loop so that if a wake-up > + * event occurred during the processing of the prcm interrupt handler > + * (setting a bit in the corresponding PM_WKST_x register and thus > + * preventing us from clearing bit 0 of the PRM_IRQSTATUS_MPU register) > + * this would be handled. > + */ > +static void prcm_irq_handler(unsigned int irq, struct irq_desc *desc) > +{ > + int i; > + unsigned long pending[OMAP_PRCM_MAX_NR_PENDING_REG]; > + unsigned long priority_pending[OMAP_PRCM_MAX_NR_PENDING_REG]; > + struct irq_chip *chip = irq_desc_get_chip(desc); > + unsigned int virtirq; > + int nr_irqs = prm_dev.irq_setup->nr_regs * 32; > + > + if (prm_dev.suspended) > + for (i = 0; i < prm_dev.irq_setup->nr_regs; i++) { > + prm_dev.saved_ena[i] = > + prm_read_reg(prm_dev.irq_setup->mask + i * 4); > + prm_write_reg(0, prm_dev.irq_setup->mask + i * 4); > + } > + > + /* > + * Loop until all pending irqs are handled, since > + * generic_handle_irq() can cause new irqs to come > + */ > + while (!prm_dev.suspended) { > + prm_pending_events(pending); > + > + /* No bit set, then all IRQs are handled */ > + if (find_first_bit(pending, nr_irqs) >= nr_irqs) > + break; > + > + prm_events_filter_priority(pending, priority_pending); > + > + /* > + * Loop on all currently pending irqs so that new irqs > + * cannot starve previously pending irqs > + */ > + > + /* Serve priority events first */ > + for_each_set_bit(virtirq, priority_pending, nr_irqs) > + generic_handle_irq(prm_dev.base_irq + virtirq); > + > + /* Serve normal events next */ > + for_each_set_bit(virtirq, pending, nr_irqs) > + generic_handle_irq(prm_dev.base_irq + virtirq); > + } > + if (chip->irq_ack) > + chip->irq_ack(&desc->irq_data); > + if (chip->irq_eoi) > + chip->irq_eoi(&desc->irq_data); > + chip->irq_unmask(&desc->irq_data); > +} > + > +/* > + * Given a PRCM event name, returns the corresponding IRQ on which the > + * handler should be registered. > + */ > +int omap_prcm_event_to_irq(const char *name) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(omap_prcm_irqs); i++) > + if (!strcmp(omap_prcm_irqs[i].name, name)) > + return prm_dev.base_irq + omap_prcm_irqs[i].offset; > + > + return -ENOENT; > +} > + > +/* > + * Reverses memory allocated and other setups done by > + * omap_prcm_irq_init(). > + */ > +void omap_prcm_irq_cleanup(void) > +{ > + int i; > + > + if (prm_dev.irq_chips) { > + for (i = 0; i < prm_dev.irq_setup->nr_regs; i++) { > + if (prm_dev.irq_chips[i]) > + irq_remove_generic_chip(prm_dev.irq_chips[i], > + 0xffffffff, 0, 0); > + prm_dev.irq_chips[i] = NULL; > + } > + kfree(prm_dev.irq_chips); > + prm_dev.irq_chips = NULL; > + } > + > + kfree(prm_dev.saved_ena); > + prm_dev.saved_ena = NULL; > + > + kfree(prm_dev.priority_mask); > + prm_dev.priority_mask = NULL; > + > + irq_set_chained_handler(prm_dev.irq, NULL); > + > + if (prm_dev.base_irq > 0) > + irq_free_descs(prm_dev.base_irq, > + prm_dev.irq_setup->nr_regs * 32); > + prm_dev.base_irq = 0; > +} > + > +/* > + * Prepare the array of PRCM events corresponding to the current SoC, > + * and set-up the chained interrupt handler mechanism. > + */ > +static int __init omap_prcm_irq_init(void) > +{ > + int i; > + struct irq_chip_generic *gc; > + struct irq_chip_type *ct; > + u32 mask[OMAP_PRCM_MAX_NR_PENDING_REG]; > + int offset; > + int max_irq = 0; > + > + prm_dev.irq_chips = kzalloc(sizeof(void *) * prm_dev.irq_setup->nr_regs, > + GFP_KERNEL); > + > + prm_dev.saved_ena = kzalloc(sizeof(u32) * prm_dev.irq_setup->nr_regs, > + GFP_KERNEL); > + > + prm_dev.priority_mask = kzalloc(sizeof(u32) * > + prm_dev.irq_setup->nr_regs, GFP_KERNEL); > + > + if (!prm_dev.irq_chips || !prm_dev.saved_ena || > + !prm_dev.priority_mask) { > + pr_err("PRCM: kzalloc failed\n"); > + goto err; > + } > + > + memset(mask, 0, sizeof(mask)); > + for (i = 0; i < ARRAY_SIZE(omap_prcm_irqs); i++) > + if (prm_dev.revision & omap_prcm_irqs[i].supported_rev) { > + offset = omap_prcm_irqs[i].offset; > + mask[offset >> 5] |= 1 << (offset & 0x1f); > + if (offset > max_irq) > + max_irq = offset; > + if (omap_prcm_irqs[i].priority) > + prm_dev.priority_mask[offset >> 5] |= > + 1 << (offset & 0x1f); > + } > + > + irq_set_chained_handler(prm_dev.irq, prcm_irq_handler); > + > + prm_dev.base_irq = > + irq_alloc_descs(-1, 0, prm_dev.irq_setup->nr_regs * 32, 0); > + > + if (prm_dev.base_irq < 0) { > + pr_err("PRCM: failed to allocate irq descs\n"); > + goto err; > + } > + > + for (i = 0; i <= prm_dev.irq_setup->nr_regs; i++) { > + gc = irq_alloc_generic_chip("PRCM", 1, > + prm_dev.base_irq + i * 32, prm_dev.base, > + handle_level_irq); > + > + if (!gc) { > + pr_err("PRCM: failed to allocate generic chip\n"); > + goto err; > + } > + ct = gc->chip_types; > + ct->chip.irq_ack = irq_gc_ack_set_bit; > + ct->chip.irq_mask = irq_gc_mask_clr_bit; > + ct->chip.irq_unmask = irq_gc_mask_set_bit; > + > + ct->regs.ack = prm_dev.irq_setup->ack + (i << 2); > + ct->regs.mask = prm_dev.irq_setup->mask + (i << 2); > + > + irq_setup_generic_chip(gc, mask[i], 0, IRQ_NOREQUEST, 0); > + prm_dev.irq_chips[i] = gc; > + } > + > + return 0; > + > +err: > + omap_prcm_irq_cleanup(); > + return -ENOMEM; > +} > + > +static int omap_prm_prepare(struct device *kdev) > { > + prm_dev.suspended = 1; > return 0; > } > > +static void omap_prm_complete(struct device *kdev) > +{ > + int i; > + > + prm_dev.suspended = 0; > + > + for (i = 0; i < prm_dev.irq_setup->nr_regs; i++) > + prm_write_reg(prm_dev.saved_ena[i], > + prm_dev.irq_setup->mask + i * 4); > +} > + > static int __devexit omap_prm_remove(struct platform_device *pdev) > { > return 0; > } > > +static int __init omap_prm_probe(struct platform_device *pdev) > +{ > + struct omap_hwmod *oh; > + int rev; > + > + oh = omap_hwmod_lookup("prm"); > + > + if (!oh) { > + pr_err("prm hwmod not found\n"); > + return -ENODEV; > + } > + > + prm_dev.base = omap_hwmod_get_mpu_rt_va(oh); > + > + rev = prm_read_reg(oh->class->sysc->rev_offs); > + > + switch (rev) { > + case OMAP3_PRM_REVISION: > + prm_dev.irq_setup = &omap3_prcm_irq_setup; > + prm_dev.revision = PRM_OMAP3; > + break; > + case OMAP4_PRM_REVISION: > + prm_dev.irq_setup = &omap4_prcm_irq_setup; > + prm_dev.revision = PRM_OMAP4; > + break; > + default: > + pr_err("unknown PRM revision: %08x\n", rev); > + return -ENODEV; > + } > + > + prm_dev.irq = oh->mpu_irqs[0].irq; > + > + omap_prcm_irq_init(); > + > + return 0; > +} > + > +static const struct dev_pm_ops prm_pm_ops = { > + .prepare = omap_prm_prepare, > + .complete = omap_prm_complete, > +}; > + > static struct platform_driver prm_driver = { > .remove = __exit_p(omap_prm_remove), > .driver = { > .name = DRIVER_NAME, > + .pm = &prm_pm_ops, > }, > }; > > diff --git a/include/linux/power/omap_prm.h b/include/linux/power/omap_prm.h > new file mode 100644 > index 0000000..9b161b5 > --- /dev/null > +++ b/include/linux/power/omap_prm.h > @@ -0,0 +1,19 @@ > +/* > + * OMAP Power and Reset Management (PRM) driver > + * > + * Copyright (C) 2011 Texas Instruments, Inc. > + * > + * Author: Tero Kristo <t-kristo@xxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + */ > + > +#ifndef __LINUX_POWER_OMAP_PRM_H__ > +#define __LINUX_POWER_OMAP_PRM_H__ > + > +int omap_prcm_event_to_irq(const char *name); > + > +#endif > -- > 1.7.4.1 > > > Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-omap" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > - Paul