Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Interrupt support for Cirrus Logic Madera codecs
0004  *
0005  * Copyright (C) 2015-2018 Cirrus Logic, Inc. and
0006  *                         Cirrus Logic International Semiconductor Ltd.
0007  */
0008 
0009 #include <linux/module.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/irq.h>
0012 #include <linux/irqdomain.h>
0013 #include <linux/pm_runtime.h>
0014 #include <linux/regmap.h>
0015 #include <linux/slab.h>
0016 #include <linux/of.h>
0017 #include <linux/of_device.h>
0018 #include <linux/of_irq.h>
0019 #include <linux/irqchip/irq-madera.h>
0020 #include <linux/mfd/madera/core.h>
0021 #include <linux/mfd/madera/pdata.h>
0022 #include <linux/mfd/madera/registers.h>
0023 
0024 #define MADERA_IRQ(_irq, _reg)                  \
0025     [MADERA_IRQ_ ## _irq] = {               \
0026         .reg_offset = (_reg) - MADERA_IRQ1_STATUS_2,    \
0027         .mask = MADERA_ ## _irq ## _EINT1       \
0028     }
0029 
0030 /* Mappings are the same for all Madera codecs */
0031 static const struct regmap_irq madera_irqs[MADERA_NUM_IRQ] = {
0032     MADERA_IRQ(FLL1_LOCK,       MADERA_IRQ1_STATUS_2),
0033     MADERA_IRQ(FLL2_LOCK,       MADERA_IRQ1_STATUS_2),
0034     MADERA_IRQ(FLL3_LOCK,       MADERA_IRQ1_STATUS_2),
0035     MADERA_IRQ(FLLAO_LOCK,      MADERA_IRQ1_STATUS_2),
0036 
0037     MADERA_IRQ(MICDET1,     MADERA_IRQ1_STATUS_6),
0038     MADERA_IRQ(MICDET2,     MADERA_IRQ1_STATUS_6),
0039     MADERA_IRQ(HPDET,       MADERA_IRQ1_STATUS_6),
0040 
0041     MADERA_IRQ(MICD_CLAMP_RISE, MADERA_IRQ1_STATUS_7),
0042     MADERA_IRQ(MICD_CLAMP_FALL, MADERA_IRQ1_STATUS_7),
0043     MADERA_IRQ(JD1_RISE,        MADERA_IRQ1_STATUS_7),
0044     MADERA_IRQ(JD1_FALL,        MADERA_IRQ1_STATUS_7),
0045 
0046     MADERA_IRQ(ASRC2_IN1_LOCK,  MADERA_IRQ1_STATUS_9),
0047     MADERA_IRQ(ASRC2_IN2_LOCK,  MADERA_IRQ1_STATUS_9),
0048     MADERA_IRQ(ASRC1_IN1_LOCK,  MADERA_IRQ1_STATUS_9),
0049     MADERA_IRQ(ASRC1_IN2_LOCK,  MADERA_IRQ1_STATUS_9),
0050     MADERA_IRQ(DRC2_SIG_DET,    MADERA_IRQ1_STATUS_9),
0051     MADERA_IRQ(DRC1_SIG_DET,    MADERA_IRQ1_STATUS_9),
0052 
0053     MADERA_IRQ(DSP_IRQ1,        MADERA_IRQ1_STATUS_11),
0054     MADERA_IRQ(DSP_IRQ2,        MADERA_IRQ1_STATUS_11),
0055     MADERA_IRQ(DSP_IRQ3,        MADERA_IRQ1_STATUS_11),
0056     MADERA_IRQ(DSP_IRQ4,        MADERA_IRQ1_STATUS_11),
0057     MADERA_IRQ(DSP_IRQ5,        MADERA_IRQ1_STATUS_11),
0058     MADERA_IRQ(DSP_IRQ6,        MADERA_IRQ1_STATUS_11),
0059     MADERA_IRQ(DSP_IRQ7,        MADERA_IRQ1_STATUS_11),
0060     MADERA_IRQ(DSP_IRQ8,        MADERA_IRQ1_STATUS_11),
0061     MADERA_IRQ(DSP_IRQ9,        MADERA_IRQ1_STATUS_11),
0062     MADERA_IRQ(DSP_IRQ10,       MADERA_IRQ1_STATUS_11),
0063     MADERA_IRQ(DSP_IRQ11,       MADERA_IRQ1_STATUS_11),
0064     MADERA_IRQ(DSP_IRQ12,       MADERA_IRQ1_STATUS_11),
0065     MADERA_IRQ(DSP_IRQ13,       MADERA_IRQ1_STATUS_11),
0066     MADERA_IRQ(DSP_IRQ14,       MADERA_IRQ1_STATUS_11),
0067     MADERA_IRQ(DSP_IRQ15,       MADERA_IRQ1_STATUS_11),
0068     MADERA_IRQ(DSP_IRQ16,       MADERA_IRQ1_STATUS_11),
0069 
0070     MADERA_IRQ(HP3R_SC,     MADERA_IRQ1_STATUS_12),
0071     MADERA_IRQ(HP3L_SC,     MADERA_IRQ1_STATUS_12),
0072     MADERA_IRQ(HP2R_SC,     MADERA_IRQ1_STATUS_12),
0073     MADERA_IRQ(HP2L_SC,     MADERA_IRQ1_STATUS_12),
0074     MADERA_IRQ(HP1R_SC,     MADERA_IRQ1_STATUS_12),
0075     MADERA_IRQ(HP1L_SC,     MADERA_IRQ1_STATUS_12),
0076 
0077     MADERA_IRQ(SPK_OVERHEAT_WARN,   MADERA_IRQ1_STATUS_15),
0078     MADERA_IRQ(SPK_OVERHEAT,    MADERA_IRQ1_STATUS_15),
0079 
0080     MADERA_IRQ(DSP1_BUS_ERR,    MADERA_IRQ1_STATUS_33),
0081     MADERA_IRQ(DSP2_BUS_ERR,    MADERA_IRQ1_STATUS_33),
0082     MADERA_IRQ(DSP3_BUS_ERR,    MADERA_IRQ1_STATUS_33),
0083     MADERA_IRQ(DSP4_BUS_ERR,    MADERA_IRQ1_STATUS_33),
0084     MADERA_IRQ(DSP5_BUS_ERR,    MADERA_IRQ1_STATUS_33),
0085     MADERA_IRQ(DSP6_BUS_ERR,    MADERA_IRQ1_STATUS_33),
0086     MADERA_IRQ(DSP7_BUS_ERR,    MADERA_IRQ1_STATUS_33),
0087 };
0088 
0089 static const struct regmap_irq_chip madera_irq_chip = {
0090     .name       = "madera IRQ",
0091     .status_base    = MADERA_IRQ1_STATUS_2,
0092     .mask_base  = MADERA_IRQ1_MASK_2,
0093     .ack_base   = MADERA_IRQ1_STATUS_2,
0094     .runtime_pm = true,
0095     .num_regs   = 32,
0096     .irqs       = madera_irqs,
0097     .num_irqs   = ARRAY_SIZE(madera_irqs),
0098 };
0099 
0100 #ifdef CONFIG_PM_SLEEP
0101 static int madera_suspend(struct device *dev)
0102 {
0103     struct madera *madera = dev_get_drvdata(dev->parent);
0104 
0105     dev_dbg(madera->irq_dev, "Suspend, disabling IRQ\n");
0106 
0107     /*
0108      * A runtime resume would be needed to access the chip interrupt
0109      * controller but runtime pm doesn't function during suspend.
0110      * Temporarily disable interrupts until we reach suspend_noirq state.
0111      */
0112     disable_irq(madera->irq);
0113 
0114     return 0;
0115 }
0116 
0117 static int madera_suspend_noirq(struct device *dev)
0118 {
0119     struct madera *madera = dev_get_drvdata(dev->parent);
0120 
0121     dev_dbg(madera->irq_dev, "No IRQ suspend, reenabling IRQ\n");
0122 
0123     /* Re-enable interrupts to service wakeup interrupts from the chip */
0124     enable_irq(madera->irq);
0125 
0126     return 0;
0127 }
0128 
0129 static int madera_resume_noirq(struct device *dev)
0130 {
0131     struct madera *madera = dev_get_drvdata(dev->parent);
0132 
0133     dev_dbg(madera->irq_dev, "No IRQ resume, disabling IRQ\n");
0134 
0135     /*
0136      * We can't handle interrupts until runtime pm is available again.
0137      * Disable them temporarily.
0138      */
0139     disable_irq(madera->irq);
0140 
0141     return 0;
0142 }
0143 
0144 static int madera_resume(struct device *dev)
0145 {
0146     struct madera *madera = dev_get_drvdata(dev->parent);
0147 
0148     dev_dbg(madera->irq_dev, "Resume, reenabling IRQ\n");
0149 
0150     /* Interrupts can now be handled */
0151     enable_irq(madera->irq);
0152 
0153     return 0;
0154 }
0155 #endif
0156 
0157 static const struct dev_pm_ops madera_irq_pm_ops = {
0158     SET_SYSTEM_SLEEP_PM_OPS(madera_suspend, madera_resume)
0159     SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(madera_suspend_noirq,
0160                       madera_resume_noirq)
0161 };
0162 
0163 static int madera_irq_probe(struct platform_device *pdev)
0164 {
0165     struct madera *madera = dev_get_drvdata(pdev->dev.parent);
0166     struct irq_data *irq_data;
0167     unsigned int irq_flags = 0;
0168     int ret;
0169 
0170     dev_dbg(&pdev->dev, "probe\n");
0171 
0172     /*
0173      * Read the flags from the interrupt controller if not specified
0174      * by pdata
0175      */
0176     irq_flags = madera->pdata.irq_flags;
0177     if (!irq_flags) {
0178         irq_data = irq_get_irq_data(madera->irq);
0179         if (!irq_data) {
0180             dev_err(&pdev->dev, "Invalid IRQ: %d\n", madera->irq);
0181             return -EINVAL;
0182         }
0183 
0184         irq_flags = irqd_get_trigger_type(irq_data);
0185 
0186         /* Codec defaults to trigger low, use this if no flags given */
0187         if (irq_flags == IRQ_TYPE_NONE)
0188             irq_flags = IRQF_TRIGGER_LOW;
0189     }
0190 
0191     if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
0192         dev_err(&pdev->dev, "Host interrupt not level-triggered\n");
0193         return -EINVAL;
0194     }
0195 
0196     /*
0197      * The silicon always starts at active-low, check if we need to
0198      * switch to active-high.
0199      */
0200     if (irq_flags & IRQF_TRIGGER_HIGH) {
0201         ret = regmap_update_bits(madera->regmap, MADERA_IRQ1_CTRL,
0202                      MADERA_IRQ_POL_MASK, 0);
0203         if (ret) {
0204             dev_err(&pdev->dev,
0205                 "Failed to set IRQ polarity: %d\n", ret);
0206             return ret;
0207         }
0208     }
0209 
0210     /*
0211      * NOTE: regmap registers this against the OF node of the parent of
0212      * the regmap - that is, against the mfd driver
0213      */
0214     ret = regmap_add_irq_chip(madera->regmap, madera->irq, IRQF_ONESHOT, 0,
0215                   &madera_irq_chip, &madera->irq_data);
0216     if (ret) {
0217         dev_err(&pdev->dev, "add_irq_chip failed: %d\n", ret);
0218         return ret;
0219     }
0220 
0221     /* Save dev in parent MFD struct so it is accessible to siblings */
0222     madera->irq_dev = &pdev->dev;
0223 
0224     return 0;
0225 }
0226 
0227 static int madera_irq_remove(struct platform_device *pdev)
0228 {
0229     struct madera *madera = dev_get_drvdata(pdev->dev.parent);
0230 
0231     /*
0232      * The IRQ is disabled by the parent MFD driver before
0233      * it starts cleaning up all child drivers
0234      */
0235     madera->irq_dev = NULL;
0236     regmap_del_irq_chip(madera->irq, madera->irq_data);
0237 
0238     return 0;
0239 }
0240 
0241 static struct platform_driver madera_irq_driver = {
0242     .probe  = &madera_irq_probe,
0243     .remove = &madera_irq_remove,
0244     .driver = {
0245         .name   = "madera-irq",
0246         .pm = &madera_irq_pm_ops,
0247     }
0248 };
0249 module_platform_driver(madera_irq_driver);
0250 
0251 MODULE_SOFTDEP("pre: madera");
0252 MODULE_DESCRIPTION("Madera IRQ driver");
0253 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
0254 MODULE_LICENSE("GPL v2");