[Qualcomm PM8921 MFD v2 2/6] mfd: pm8xxx: Add irq support
Abhijeet Dharmapurikar
adharmap at codeaurora.org
Wed Mar 9 00:21:11 EST 2011
Thomas Gleixner wrote:
> On Mon, 7 Mar 2011, adharmap at codeaurora.org wrote:
>> +static int __devinit pm8921_add_subdevices(const struct pm8921_platform_data
>> + *pdata,
>> + struct pm8921 *pmic,
>> + u32 rev)
>> +{
>> + int ret = 0;
>> + int irq_base = 0;
>> + void *irq_data;
>> +
>> + if (pdata->irq_pdata) {
>
> So if pdata->irq_pdata == NULL you return success. Is that correct ?
Yes. The board configuration may choose not to use pmic interrupts.
> Also please return early on (!pdata->irq_pdata) and avoid that extra
> indent level for the real code path.
I did not do that because there are other subdevices that I will be
adding in the later patches. I cannot return early. well I will change
it for this patch.
>
>> + pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
>> + pdata->irq_pdata->irq_cdata.rev = rev;
>> + irq_base = pdata->irq_pdata->irq_base;
>> + irq_data = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
>> +
>> + if (IS_ERR(irq_data)) {
>> + pr_err("Failed to init interrupts ret=%ld\n",
>> + PTR_ERR(irq_data));
>> + ret = PTR_ERR(irq_data);
>> + goto bail;
>
> return PTR_ERR(irq_data);
>
> And then you have
> }
> pmic->irq_data = irq_data;
> return 0;
>
>> + } else
>> + pmic->irq_data = irq_data;
>> + }
>> +
>> +bail:
>> + return ret;
>> +}
Ok will do it in the next patchset.
>
>
>> +static int
>> +pm8xxx_read_block_irq(const struct pm_irq_chip *chip, u8 bp, u8 *ip)
>> +{
>> + int rc;
>> +
>> + rc = pm8xxx_writeb(chip->dev,
>> + SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
>> + if (rc) {
>> + pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
>> + goto bail_out;
>
> These goto's are silly. return rc; is fine here.
Sure, now that I realize SSBI_REG_ADDR_IRQ_BLK_SEL would need
protection. Will add locking
>
>> +static int
>> +pm8xxx_config_irq(const struct pm_irq_chip *chip, u8 bp, u8 cp)
>> +{
>> + int rc;
>> +
>> + rc = pm8xxx_writeb(chip->dev,
>> + SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
>
> And how are the callers of this function serialized against the other
> functions which access SSBI_REG_ADDR_IRQ_BLK_SEL ?
Thanks for pointing this out, will add locks around these.
>
>> + if (rc) {
>> + pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
>> + goto bail_out;
>> + }
>> +
>> + rc = pm8xxx_writeb(chip->dev,
>> + SSBI_REG_ADDR_IRQ_CONFIG, cp);
>> + if (rc)
>> + pr_err("Failed Configuring IRQ rc=%d\n", rc);
>> +bail_out:
>> + return rc;
>> +}
>> +
>> +static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip,
>> + int block, int *handled)
>> +{
>> + int ret = 0;
>> + u8 bits;
>> + int pmirq, irq, k;
>
> Can you please collapse all int variables into one line. Also why are
> the iterators in your various functions randomly named i, j, k and
> whatever? We usually use i for the first iterator and j when we have a
> nested section.
Ok will clean this up.
>
>> + spin_lock(&chip->pm_irq_lock);
>> + ret = pm8xxx_read_block_irq(chip, block, &bits);
>> + if (ret) {
>> + if (pm8xxx_can_print())
>> + pr_err("Failed reading %d block ret=%d",
>> + block, ret);
>> + goto out;
>> + }
>
> You can drop chip->pm_irq_lock here and return if (!bits)
>
>> + if (!bits) {
>> + if (pm8xxx_can_print())
>> + pr_err("block bit set in master but no irqs: %d",
>> + block);
>> + goto out;
>> + }
>> +
>> + /* Check IRQ bits */
>> + for (k = 0; k < 8; k++) {
>> + if (bits & (1 << k)) {
>> + pmirq = block * 8 + k;
>> + irq = pmirq + chip->irq_base;
>> + chip->irqs_to_handle[*handled] = irq;
>> + (*handled)++;
>
> Why all this horrible indirection? Why don't you call
> generic_handle_irq() right away?
Will change it to call generic_handle_irq() right here.
>
>> + }
>> + }
>> +out:
>> + spin_unlock(&chip->pm_irq_lock);
>> + return ret;
>> +}
>> +
>> +static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip,
>> + int master, int *handled)
>> +{
>> + int ret = 0;
>> + u8 blockbits;
>> + int block_number, j;
>> +
>> + ret = pm8xxx_read_master_irq(chip, master, &blockbits);
>> + if (ret) {
>> + pr_err("Failed to read master %d ret=%d\n", master, ret);
>> + return ret;
>> + }
>> + if (!blockbits) {
>> + if (pm8xxx_can_print())
>
> What's the point of this ratelimit? This should not happen at all and
> if it happens often enough that you need a rate limit then you better
> figure out why and fix the real problem instead of papering over it.
There is no issue, we had one bringup where we got a lot of spurious
interrupts and these are remains from the debug code we added in. Will
clean it up.
>
>> + pr_err("master bit set in root but no blocks: %d",
>> + master);
>> + return 0;
>> + }
>> +
>> + for (j = 0; j < 8; j++)
>> + if (blockbits & (1 << j)) {
>> + block_number = master * 8 + j; /* block # */
>> + ret |= pm8xxx_irq_block_handler(chip, block_number,
>> + handled);
>> + }
>> + return ret;
>> +}
>> +
>> +static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
>> +{
>> + struct pm_irq_chip *chip = get_irq_data(irq);
>> + int i, ret;
>> + u8 root;
>> + int masters = 0, handled = 0;
>> +
>> + ret = pm8xxx_read_root_irq(chip, &root);
>> + if (ret) {
>> + pr_err("Can't read root status ret=%d\n", ret);
>> + return;
>> + }
>> +
>> + /* on pm8xxx series masters start from bit 1 of the root */
>> + masters = root >> 1;
>> +
>> + /* Read allowed masters for blocks. */
>> + for (i = 0; i < chip->num_masters; i++)
>> + if (masters & (1 << i))
>> + pm8xxx_irq_master_handler(chip, i, &handled);
>> +
>> + for (i = 0; i < handled; i++)
>> + generic_handle_irq(chip->irqs_to_handle[i]);
>> +
>> + desc->chip->ack(irq);
>
> chip->irq_ack()
Yes will do.
>
>> +}
>> +
>> +static void pm8xxx_irq_ack(struct irq_data *d)
>> +{
>> + const struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
>> + unsigned int pmirq = d->irq - chip->irq_base;
>> + u8 block, config;
>> +
>> + block = pmirq / 8;
>> +
>> + config = PM_IRQF_WRITE | chip->config[pmirq] | PM_IRQF_CLR;
>> + /* Keep the mask */
>> + if (!(chip->irqs_allowed[block] & (1 << (pmirq % 8))))
>> + config |= PM_IRQF_MASK_FE | PM_IRQF_MASK_RE;
>
> What's the point of this exercise? ack is called before mask and it
The register design is such that we cannot only clear the interrupt. One
has to write to the trigger bits while clearing it. Now trigger bits
define whether the interrupt is masked or unmasked. If unmasked they
define whether the interrupt rising/falling/level high/level low triggered.
So the code remembers which interrupts are masked and for them it clears
and rewrite the masked status in trigger bits. For unmasked ones it
clears and writes to the trigger bits essentially configuring them same
way as it was before. That is why the if satement to check interrupt was
masked earlier, chip->irqs_allowed[] maintains which interrupt are unmasked.
> ack is called before mask and it
> should never be called when the interrupt is masked.
I didnt quite understand this comment. handle_level_irq calls mask_ack
which masks the interrupt and then acks it. In this case the ack is
called after the mask. Moreover, handle_edge_irq calls only ack and the
interrupt stays unmasked. I dont see ack is always associated with a
mask or any ordering is enforced between them. Please clarify.
>> + config = PM_IRQF_WRITE | chip->config[pmirq] |
>> + PM_IRQF_MASK_FE | PM_IRQF_MASK_RE;
>
> Why don't you define PM_IRQF_MASK as PM_IRQF_MASK_FE | PM_IRQF_MASK_RE
> and use this instead of having those line breaks.
Ok will do that.
>
> Also every function which calls pm8xxx_config_irq() ORs
> PM_IRQF_WRITE. Why can't you do that in pm8xxx_config_irq() and only
> OR the real relevant bits in the various callers ?
Ok will do that.
>> +
>> + old_irqs_allowed = chip->irqs_allowed[block];
>
> ???
will remove, some debug code remnants.
>
>> + }
>> +
>> + config = PM_IRQF_WRITE
>> + | chip->config[pmirq] | PM_IRQF_CLR;
>
> Grrr. These random line breaks all over the place are horrible.
>
> Also please make that:
>
> cfg = chip->config[pmirq] | PM_IRQF_WRITE | PM_IRQF_CLR;
>
> So all the bits which you OR to the stored config are together.
Yes I tend to order them the way they show up in the register
definitions. Agree it is not readable, will fix it.
>> +int pm8xxx_get_irq_stat(void *data, int irq)
>> +{
>> + struct pm_irq_chip *chip = data;
>> + int pmirq;
>> + int rc;
>> + u8 block, bits, bit;
>> + unsigned long flags;
>> +
>> + if (chip == NULL || irq < chip->irq_base ||
>> + irq >= chip->irq_base + chip->num_irqs)
>> + return -EINVAL;
>> +
>> + pmirq = irq - chip->irq_base;
>> +
>> + block = pmirq / 8;
>> + bit = pmirq % 8;
>> +
>> + spin_lock_irqsave(&chip->pm_irq_lock, flags);
>> +
>> + rc = pm8xxx_writeb(chip->dev,
>> + SSBI_REG_ADDR_IRQ_BLK_SEL, block);
>> + if (rc) {
>> + pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
>> + irq, pmirq, block, rc);
>> + goto bail_out;
>> + }
>> +
>> + rc = pm8xxx_readb(chip->dev,
>> + SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
>> + if (rc) {
>> + pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
>> + irq, pmirq, block, rc);
>> + goto bail_out;
>> + }
>> +
>> + rc = (bits & (1 << bit)) ? 1 : 0;
>> +
>> +bail_out:
>> + spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
>> +
>> + return rc;
>> +}
>> +EXPORT_SYMBOL(pm8xxx_get_irq_stat);
>
> EXPORT_SYMBOL_GPL if at all. Why needs this to be exported?
The core driver calls this to read the status on a gpio/mpp line. The
core driver can be compiled as a module, hence the export. Will use
EXPORT_SYMBOL_GPL instead. I realize there are few more functions as
well that need exporting, will export them in the coming patch.
>
>> +
>> +
>> +void pm8xxx_show_resume_irq(void)
>> +{
>> + struct pm_irq_chip *chip;
>> + u8 block, bits;
>> + int pmirq;
>> +
>> + list_for_each_entry(chip, &pm_irq_chips, link) {
>> + for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
>> + if (test_bit(pmirq, chip->wake_enable)) {
>> + block = pmirq / 8;
>> + if (!pm8xxx_read_block_irq(chip,
>> + &block, &bits)) {
>> + if (bits & (1 << (pmirq & 0x7)))
>> + pr_warning("%d triggered\n",
>> + pmirq + chip->pdata.irq_base);
>> + }
>> + }
>> + }
>> + }
>> +}
>
> What's the point of this function?
This function is used by the power management code right before it
returns from suspend_ops->enter. It helps in debugging what exact
interrupts triggered the resume.
>
>> +int pm8xxx_resume_irq(const void *data)
>> +{
>> + const struct pm_irq_chip *chip = data;
>> + int pmirq;
>> +
>> + for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
>> + if (chip->config[i] && !test_bit(i, chip->wake_enable)) {
>> + if (!((chip->config[i] & PM_IRQF_MASK_ALL)
>> + == PM_IRQF_MASK_ALL)) {
>> + irq = i + chip->irq_base;
>> + pm8xxx_irq_unmask(get_irq_data(irq));
>> + }
>> + }
>> + }
>> +
>> + if (!chip->count_wakeable)
>> + enable_irq(chip->dev->irq);
>> +
>> + return 0;
>> +}
>> +#else
>> +#define pm8xxx_suspend NULL
>> +#define pm8xxx_resume NULL
>
> Where is pm8xxx_suspend/pm8xxx_resume defined for the !PM case and
> where are those used at all ?
The core driver calls them (these need to be export_symbol_gpled as
well). I should provide empty implementations rather than NULL
definitions. Will fix them.
>
>> +#endif
>> +
>> +void * __devinit pm8xxx_irq_init(struct device *dev,
>> + const struct pm8xxx_irq_platform_data *pdata)
>> +{
>> + struct pm_irq_chip *chip;
>> + int devirq;
>> + int rc;
>> + unsigned int pmirq;
>> +
>> + if (!pdata) {
>> + pr_err("No platform data\n");
>> + return ERR_PTR(-EINVAL);
>> + }
>> +
>> + devirq = pdata->devirq;
>> + if (devirq < 0) {
>> + pr_err("missing devirq\n");
>> + rc = devirq;
>> + goto out;
>> + }
>> +
>> + chip = kzalloc(sizeof(struct pm_irq_chip), GFP_KERNEL);
>> + if (!chip) {
>> + pr_err("Cannot alloc pm_irq_chip struct\n");
>> + rc = -ENOMEM;
>> + goto out;
>> + }
>> +
>> + chip->dev = dev;
>> + chip->devirq = devirq;
>> + chip->irq_base = pdata->irq_base;
>> + chip->num_irqs = pdata->irq_cdata.nirqs;
>> + chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
>> + chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
>> + spin_lock_init(&chip->pm_irq_lock);
>> +
>> + chip->irqs_allowed = kzalloc(sizeof(u8) * chip->num_blocks, GFP_KERNEL);
>> + if (!chip->irqs_allowed) {
>> + pr_err("Cannot alloc irqs_allowed array\n");
>> + rc = -ENOMEM;
>> + goto free_pm_irq_chip;
>> + }
>> +
>> + chip->irqs_to_handle = kzalloc(sizeof(u16) * chip->num_irqs,
>> + GFP_KERNEL);
>> + if (!chip->irqs_to_handle) {
>> + pr_err("Cannot alloc irqs_to_handle array\n");
>> + rc = -ENOMEM;
>> + goto free_irqs_allowed;
>> + }
>> + chip->config = kzalloc(sizeof(u8) * chip->num_irqs, GFP_KERNEL);
>> + if (!chip->config) {
>> + pr_err("Cannot alloc config array\n");
>> + rc = -ENOMEM;
>> + goto free_irqs_to_handle;
>> + }
>> + chip->wake_enable = kzalloc(sizeof(unsigned long)
>> + * DIV_ROUND_UP(chip->num_irqs, BITS_PER_LONG),
>> + GFP_KERNEL);
>> + if (!chip->wake_enable) {
>> + pr_err("Cannot alloc wake_enable array\n");
>> + rc = -ENOMEM;
>> + goto free_config;
>> + }
>> + list_add(&chip->link, &pm_irq_chips);
>
> What's that list for and how is it protected ?
The list is used in pm8xxx_show_resume_irq function to go over all he
chips and see what interrupts have triggered.
I think I will clean up the printing of resume interrupts and will
submit them as a separate patch along with the power management code.
This list and pm8xxx_show_resume_irq goes.
>> +
>> +free_config:
>> + kfree(chip->config);
>> +free_irqs_to_handle:
>> + kfree(chip->irqs_to_handle);
>> +free_irqs_allowed:
>> + kfree(chip->irqs_allowed);
>
> No need for 3 separate labels. You kzalloc() chip, so you can call
> kfree() on chip->xxx unconditionally.
ok, will do.
>
>> +free_pm_irq_chip:
>> + kfree(chip);
>> +out:
>> + return ERR_PTR(rc);
>> +}
>
>> +#ifdef CONFIG_MFD_PM8XXX_IRQ
>> +/**
>> + * pm8xxx_get_irq_stat - get the status of the irq line
>> + * @dev: the interrupt device
>> + * @irq: the irq number
>> + *
>> + * The pm8xxx gpio and mpp rely on the interrupt block to read
>> + * the values on their pins. This function is to facilitate reading
>> + * the status of a gpio or an mpp line. The caller has to convert the
>> + * gpio number to irq number.
>> + *
>> + * RETURNS:
>> + * an int indicating the value read on that line
>> + */
>
> Please move that comment into the implementation.
Will do.
>
>> +int pm8xxx_get_irq_stat(void *data, int irq);
>
> Thanks,
>
> tglx
--
--
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm
Innovation Center, Inc. is a member of the Code Aurora Forum.
More information about the linux-arm-kernel
mailing list