Messages in this thread | | | Subject | Re: [PATCH v12] irqchip: Add driver for Cirrus Logic Madera codecs | From | Richard Fitzgerald <> | Date | Fri, 14 Sep 2018 16:29:24 +0100 |
| |
On 14/09/18 16:20, Richard Fitzgerald wrote: > The Cirrus Logic Madera codecs (Cirrus Logic CS47L35/85/90/91 and WM1840) > are highly complex devices containing up to 7 programmable DSPs and many > other internal sources of interrupts plus a number of GPIOs that can be > used as interrupt inputs. The large number (>150) of internal interrupt > sources are managed by an on-board interrupt controller. > > This driver provides the handling for the interrupt controller. As the > codec is accessed via regmap, we can make use of the generic IRQ > functionality from regmap to do most of the work. Only around half of > the possible interrupt source are currently of interest from the driver > so only this subset is defined. Others can be added in future if needed. > > The KConfig options are not user-configurable because this driver is > mandatory so is automatically included when the parent MFD driver is > selected. > > Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com> > Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com> > --- > Only difference from v11 is the copyright header.
Sorry everyone, ignore this version and use v13. I forgot to change the copyright on the .h file. Friday afternoon is a bad time to send patches.
> --- > MAINTAINERS | 2 + > drivers/irqchip/Kconfig | 3 + > drivers/irqchip/Makefile | 1 + > drivers/irqchip/irq-madera.c | 256 +++++++++++++++++++++++++++++++++++++ > include/linux/irqchip/irq-madera.h | 135 +++++++++++++++++++ > 5 files changed, 397 insertions(+) > create mode 100644 drivers/irqchip/irq-madera.c > create mode 100644 include/linux/irqchip/irq-madera.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index bb65f0c1861c..faff2f72ab29 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -3595,8 +3595,10 @@ W: https://github.com/CirrusLogic/linux-drivers/wiki > S: Supported > F: Documentation/devicetree/bindings/mfd/madera.txt > F: Documentation/devicetree/bindings/pinctrl/cirrus,madera-pinctrl.txt > +F: include/linux/irqchip/irq-madera* > F: include/linux/mfd/madera/* > F: drivers/gpio/gpio-madera* > +F: drivers/irqchip/irq-madera* > F: drivers/mfd/madera* > F: drivers/mfd/cs47l* > F: drivers/pinctrl/cirrus/* > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig > index 383e7b70221d..0afe6bdd2074 100644 > --- a/drivers/irqchip/Kconfig > +++ b/drivers/irqchip/Kconfig > @@ -150,6 +150,9 @@ config IMGPDC_IRQ > select GENERIC_IRQ_CHIP > select IRQ_DOMAIN > > +config MADERA_IRQ > + tristate > + > config IRQ_MIPS_CPU > bool > select GENERIC_IRQ_CHIP > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile > index fbd1ec8070ef..97838212d8e4 100644 > --- a/drivers/irqchip/Makefile > +++ b/drivers/irqchip/Makefile > @@ -88,3 +88,4 @@ obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o > obj-$(CONFIG_NDS32) += irq-ativic32.o > obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o > obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o > +obj-$(CONFIG_MADERA_IRQ) += irq-madera.o > diff --git a/drivers/irqchip/irq-madera.c b/drivers/irqchip/irq-madera.c > new file mode 100644 > index 000000000000..e9256dee1a45 > --- /dev/null > +++ b/drivers/irqchip/irq-madera.c > @@ -0,0 +1,256 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Interrupt support for Cirrus Logic Madera codecs > + * > + * Copyright (C) 2015-2018 Cirrus Logic, Inc. and > + * Cirrus Logic International Semiconductor Ltd. > + */ > + > +#include <linux/module.h> > +#include <linux/gpio.h> > +#include <linux/interrupt.h> > +#include <linux/irq.h> > +#include <linux/irqdomain.h> > +#include <linux/pm_runtime.h> > +#include <linux/regmap.h> > +#include <linux/slab.h> > +#include <linux/of.h> > +#include <linux/of_device.h> > +#include <linux/of_gpio.h> > +#include <linux/of_irq.h> > +#include <linux/irqchip/irq-madera.h> > +#include <linux/mfd/madera/core.h> > +#include <linux/mfd/madera/pdata.h> > +#include <linux/mfd/madera/registers.h> > + > +#define MADERA_IRQ(_irq, _reg) \ > + [MADERA_IRQ_ ## _irq] = { \ > + .reg_offset = (_reg) - MADERA_IRQ1_STATUS_2, \ > + .mask = MADERA_ ## _irq ## _EINT1 \ > + } > + > +/* Mappings are the same for all Madera codecs */ > +static const struct regmap_irq madera_irqs[MADERA_NUM_IRQ] = { > + MADERA_IRQ(FLL1_LOCK, MADERA_IRQ1_STATUS_2), > + MADERA_IRQ(FLL2_LOCK, MADERA_IRQ1_STATUS_2), > + MADERA_IRQ(FLL3_LOCK, MADERA_IRQ1_STATUS_2), > + MADERA_IRQ(FLLAO_LOCK, MADERA_IRQ1_STATUS_2), > + > + MADERA_IRQ(MICDET1, MADERA_IRQ1_STATUS_6), > + MADERA_IRQ(MICDET2, MADERA_IRQ1_STATUS_6), > + MADERA_IRQ(HPDET, MADERA_IRQ1_STATUS_6), > + > + MADERA_IRQ(MICD_CLAMP_RISE, MADERA_IRQ1_STATUS_7), > + MADERA_IRQ(MICD_CLAMP_FALL, MADERA_IRQ1_STATUS_7), > + MADERA_IRQ(JD1_RISE, MADERA_IRQ1_STATUS_7), > + MADERA_IRQ(JD1_FALL, MADERA_IRQ1_STATUS_7), > + > + MADERA_IRQ(ASRC2_IN1_LOCK, MADERA_IRQ1_STATUS_9), > + MADERA_IRQ(ASRC2_IN2_LOCK, MADERA_IRQ1_STATUS_9), > + MADERA_IRQ(ASRC1_IN1_LOCK, MADERA_IRQ1_STATUS_9), > + MADERA_IRQ(ASRC1_IN2_LOCK, MADERA_IRQ1_STATUS_9), > + MADERA_IRQ(DRC2_SIG_DET, MADERA_IRQ1_STATUS_9), > + MADERA_IRQ(DRC1_SIG_DET, MADERA_IRQ1_STATUS_9), > + > + MADERA_IRQ(DSP_IRQ1, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ2, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ3, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ4, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ5, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ6, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ7, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ8, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ9, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ10, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ11, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ12, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ13, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ14, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ15, MADERA_IRQ1_STATUS_11), > + MADERA_IRQ(DSP_IRQ16, MADERA_IRQ1_STATUS_11), > + > + MADERA_IRQ(HP3R_SC, MADERA_IRQ1_STATUS_12), > + MADERA_IRQ(HP3L_SC, MADERA_IRQ1_STATUS_12), > + MADERA_IRQ(HP2R_SC, MADERA_IRQ1_STATUS_12), > + MADERA_IRQ(HP2L_SC, MADERA_IRQ1_STATUS_12), > + MADERA_IRQ(HP1R_SC, MADERA_IRQ1_STATUS_12), > + MADERA_IRQ(HP1L_SC, MADERA_IRQ1_STATUS_12), > + > + MADERA_IRQ(SPK_OVERHEAT_WARN, MADERA_IRQ1_STATUS_15), > + MADERA_IRQ(SPK_OVERHEAT, MADERA_IRQ1_STATUS_15), > + > + MADERA_IRQ(DSP1_BUS_ERR, MADERA_IRQ1_STATUS_33), > + MADERA_IRQ(DSP2_BUS_ERR, MADERA_IRQ1_STATUS_33), > + MADERA_IRQ(DSP3_BUS_ERR, MADERA_IRQ1_STATUS_33), > + MADERA_IRQ(DSP4_BUS_ERR, MADERA_IRQ1_STATUS_33), > + MADERA_IRQ(DSP5_BUS_ERR, MADERA_IRQ1_STATUS_33), > + MADERA_IRQ(DSP6_BUS_ERR, MADERA_IRQ1_STATUS_33), > + MADERA_IRQ(DSP7_BUS_ERR, MADERA_IRQ1_STATUS_33), > +}; > + > +static const struct regmap_irq_chip madera_irq_chip = { > + .name = "madera IRQ", > + .status_base = MADERA_IRQ1_STATUS_2, > + .mask_base = MADERA_IRQ1_MASK_2, > + .ack_base = MADERA_IRQ1_STATUS_2, > + .runtime_pm = true, > + .num_regs = 32, > + .irqs = madera_irqs, > + .num_irqs = ARRAY_SIZE(madera_irqs), > +}; > + > +#ifdef CONFIG_PM_SLEEP > +static int madera_suspend(struct device *dev) > +{ > + struct madera *madera = dev_get_drvdata(dev->parent); > + > + dev_dbg(madera->irq_dev, "Suspend, disabling IRQ\n"); > + > + /* > + * A runtime resume would be needed to access the chip interrupt > + * controller but runtime pm doesn't function during suspend. > + * Temporarily disable interrupts until we reach suspend_noirq state. > + */ > + disable_irq(madera->irq); > + > + return 0; > +} > + > +static int madera_suspend_noirq(struct device *dev) > +{ > + struct madera *madera = dev_get_drvdata(dev->parent); > + > + dev_dbg(madera->irq_dev, "No IRQ suspend, reenabling IRQ\n"); > + > + /* Re-enable interrupts to service wakeup interrupts from the chip */ > + enable_irq(madera->irq); > + > + return 0; > +} > + > +static int madera_resume_noirq(struct device *dev) > +{ > + struct madera *madera = dev_get_drvdata(dev->parent); > + > + dev_dbg(madera->irq_dev, "No IRQ resume, disabling IRQ\n"); > + > + /* > + * We can't handle interrupts until runtime pm is available again. > + * Disable them temporarily. > + */ > + disable_irq(madera->irq); > + > + return 0; > +} > + > +static int madera_resume(struct device *dev) > +{ > + struct madera *madera = dev_get_drvdata(dev->parent); > + > + dev_dbg(madera->irq_dev, "Resume, reenabling IRQ\n"); > + > + /* Interrupts can now be handled */ > + enable_irq(madera->irq); > + > + return 0; > +} > +#endif > + > +static const struct dev_pm_ops madera_irq_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(madera_suspend, madera_resume) > + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(madera_suspend_noirq, > + madera_resume_noirq) > +}; > + > +static int madera_irq_probe(struct platform_device *pdev) > +{ > + struct madera *madera = dev_get_drvdata(pdev->dev.parent); > + struct irq_data *irq_data; > + unsigned int irq_flags = 0; > + int ret; > + > + dev_dbg(&pdev->dev, "probe\n"); > + > + /* > + * Read the flags from the interrupt controller if not specified > + * by pdata > + */ > + irq_flags = madera->pdata.irq_flags; > + if (!irq_flags) { > + irq_data = irq_get_irq_data(madera->irq); > + if (!irq_data) { > + dev_err(&pdev->dev, "Invalid IRQ: %d\n", madera->irq); > + return -EINVAL; > + } > + > + irq_flags = irqd_get_trigger_type(irq_data); > + > + /* Codec defaults to trigger low, use this if no flags given */ > + if (irq_flags == IRQ_TYPE_NONE) > + irq_flags = IRQF_TRIGGER_LOW; > + } > + > + if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { > + dev_err(&pdev->dev, "Host interrupt not level-triggered\n"); > + return -EINVAL; > + } > + > + /* > + * The silicon always starts at active-low, check if we need to > + * switch to active-high. > + */ > + if (irq_flags & IRQF_TRIGGER_HIGH) { > + ret = regmap_update_bits(madera->regmap, MADERA_IRQ1_CTRL, > + MADERA_IRQ_POL_MASK, 0); > + if (ret) { > + dev_err(&pdev->dev, > + "Failed to set IRQ polarity: %d\n", ret); > + return ret; > + } > + } > + > + /* > + * NOTE: regmap registers this against the OF node of the parent of > + * the regmap - that is, against the mfd driver > + */ > + ret = regmap_add_irq_chip(madera->regmap, madera->irq, IRQF_ONESHOT, 0, > + &madera_irq_chip, &madera->irq_data); > + if (ret) { > + dev_err(&pdev->dev, "add_irq_chip failed: %d\n", ret); > + return ret; > + } > + > + /* Save dev in parent MFD struct so it is accessible to siblings */ > + madera->irq_dev = &pdev->dev; > + > + return 0; > +} > + > +static int madera_irq_remove(struct platform_device *pdev) > +{ > + struct madera *madera = dev_get_drvdata(pdev->dev.parent); > + > + /* > + * The IRQ is disabled by the parent MFD driver before > + * it starts cleaning up all child drivers > + */ > + madera->irq_dev = NULL; > + regmap_del_irq_chip(madera->irq, madera->irq_data); > + > + return 0; > +} > + > +static struct platform_driver madera_irq_driver = { > + .probe = &madera_irq_probe, > + .remove = &madera_irq_remove, > + .driver = { > + .name = "madera-irq", > + .pm = &madera_irq_pm_ops, > + } > +}; > +module_platform_driver(madera_irq_driver); > + > +MODULE_SOFTDEP("pre: madera"); > +MODULE_DESCRIPTION("Madera IRQ driver"); > +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/irqchip/irq-madera.h b/include/linux/irqchip/irq-madera.h > new file mode 100644 > index 000000000000..5aeb7e6adc82 > --- /dev/null > +++ b/include/linux/irqchip/irq-madera.h > @@ -0,0 +1,135 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Interrupt support for Cirrus Logic Madera codecs > + * > + * Copyright 2016-2018 Cirrus Logic > + * > + * 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; version 2. > + */ > + > +#ifndef IRQCHIP_MADERA_H > +#define IRQCHIP_MADERA_H > + > +#include <linux/interrupt.h> > +#include <linux/mfd/madera/core.h> > + > +#define MADERA_IRQ_FLL1_LOCK 0 > +#define MADERA_IRQ_FLL2_LOCK 1 > +#define MADERA_IRQ_FLL3_LOCK 2 > +#define MADERA_IRQ_FLLAO_LOCK 3 > +#define MADERA_IRQ_CLK_SYS_ERR 4 > +#define MADERA_IRQ_CLK_ASYNC_ERR 5 > +#define MADERA_IRQ_CLK_DSP_ERR 6 > +#define MADERA_IRQ_HPDET 7 > +#define MADERA_IRQ_MICDET1 8 > +#define MADERA_IRQ_MICDET2 9 > +#define MADERA_IRQ_JD1_RISE 10 > +#define MADERA_IRQ_JD1_FALL 11 > +#define MADERA_IRQ_JD2_RISE 12 > +#define MADERA_IRQ_JD2_FALL 13 > +#define MADERA_IRQ_MICD_CLAMP_RISE 14 > +#define MADERA_IRQ_MICD_CLAMP_FALL 15 > +#define MADERA_IRQ_DRC2_SIG_DET 16 > +#define MADERA_IRQ_DRC1_SIG_DET 17 > +#define MADERA_IRQ_ASRC1_IN1_LOCK 18 > +#define MADERA_IRQ_ASRC1_IN2_LOCK 19 > +#define MADERA_IRQ_ASRC2_IN1_LOCK 20 > +#define MADERA_IRQ_ASRC2_IN2_LOCK 21 > +#define MADERA_IRQ_DSP_IRQ1 22 > +#define MADERA_IRQ_DSP_IRQ2 23 > +#define MADERA_IRQ_DSP_IRQ3 24 > +#define MADERA_IRQ_DSP_IRQ4 25 > +#define MADERA_IRQ_DSP_IRQ5 26 > +#define MADERA_IRQ_DSP_IRQ6 27 > +#define MADERA_IRQ_DSP_IRQ7 28 > +#define MADERA_IRQ_DSP_IRQ8 29 > +#define MADERA_IRQ_DSP_IRQ9 30 > +#define MADERA_IRQ_DSP_IRQ10 31 > +#define MADERA_IRQ_DSP_IRQ11 32 > +#define MADERA_IRQ_DSP_IRQ12 33 > +#define MADERA_IRQ_DSP_IRQ13 34 > +#define MADERA_IRQ_DSP_IRQ14 35 > +#define MADERA_IRQ_DSP_IRQ15 36 > +#define MADERA_IRQ_DSP_IRQ16 37 > +#define MADERA_IRQ_HP1L_SC 38 > +#define MADERA_IRQ_HP1R_SC 39 > +#define MADERA_IRQ_HP2L_SC 40 > +#define MADERA_IRQ_HP2R_SC 41 > +#define MADERA_IRQ_HP3L_SC 42 > +#define MADERA_IRQ_HP3R_SC 43 > +#define MADERA_IRQ_SPKOUTL_SC 44 > +#define MADERA_IRQ_SPKOUTR_SC 45 > +#define MADERA_IRQ_HP1L_ENABLE_DONE 46 > +#define MADERA_IRQ_HP1R_ENABLE_DONE 47 > +#define MADERA_IRQ_HP2L_ENABLE_DONE 48 > +#define MADERA_IRQ_HP2R_ENABLE_DONE 49 > +#define MADERA_IRQ_HP3L_ENABLE_DONE 50 > +#define MADERA_IRQ_HP3R_ENABLE_DONE 51 > +#define MADERA_IRQ_SPKOUTL_ENABLE_DONE 52 > +#define MADERA_IRQ_SPKOUTR_ENABLE_DONE 53 > +#define MADERA_IRQ_SPK_SHUTDOWN 54 > +#define MADERA_IRQ_SPK_OVERHEAT 55 > +#define MADERA_IRQ_SPK_OVERHEAT_WARN 56 > +#define MADERA_IRQ_GPIO1 57 > +#define MADERA_IRQ_GPIO2 58 > +#define MADERA_IRQ_GPIO3 59 > +#define MADERA_IRQ_GPIO4 60 > +#define MADERA_IRQ_GPIO5 61 > +#define MADERA_IRQ_GPIO6 62 > +#define MADERA_IRQ_GPIO7 63 > +#define MADERA_IRQ_GPIO8 64 > +#define MADERA_IRQ_DSP1_BUS_ERR 65 > +#define MADERA_IRQ_DSP2_BUS_ERR 66 > +#define MADERA_IRQ_DSP3_BUS_ERR 67 > +#define MADERA_IRQ_DSP4_BUS_ERR 68 > +#define MADERA_IRQ_DSP5_BUS_ERR 69 > +#define MADERA_IRQ_DSP6_BUS_ERR 70 > +#define MADERA_IRQ_DSP7_BUS_ERR 71 > + > +#define MADERA_NUM_IRQ 72 > + > +/* > + * These wrapper functions are for use by other child drivers of the > + * same parent MFD. > + */ > +static inline int madera_get_irq_mapping(struct madera *madera, int irq) > +{ > + if (!madera->irq_dev) > + return -ENODEV; > + > + return regmap_irq_get_virq(madera->irq_data, irq); > +} > + > +static inline int madera_request_irq(struct madera *madera, int irq, > + const char *name, > + irq_handler_t handler, void *data) > +{ > + irq = madera_get_irq_mapping(madera, irq); > + if (irq < 0) > + return irq; > + > + return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, name, > + data); > +} > + > +static inline void madera_free_irq(struct madera *madera, int irq, void *data) > +{ > + irq = madera_get_irq_mapping(madera, irq); > + if (irq < 0) > + return; > + > + free_irq(irq, data); > +} > + > +static inline int madera_set_irq_wake(struct madera *madera, int irq, int on) > +{ > + irq = madera_get_irq_mapping(madera, irq); > + if (irq < 0) > + return irq; > + > + return irq_set_irq_wake(irq, on); > +} > + > +#endif >
| |