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-kri...@ti.com>
> + *
> + * 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 majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


- Paul

Reply via email to