[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <452de3e6-face-623d-4997-26b35d0733e7@opensource.cirrus.com>
Date: Fri, 14 Sep 2018 16:29:24 +0100
From: Richard Fitzgerald <rf@...nsource.cirrus.com>
To: <tglx@...utronix.de>, <jason@...edaemon.net>,
<marc.zyngier@....com>
CC: <patches@...nsource.cirrus.com>, <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v12] irqchip: Add driver for Cirrus Logic Madera codecs
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@...nsource.cirrus.com>
> Signed-off-by: Charles Keepax <ckeepax@...nsource.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@...nsource.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
>
Powered by blists - more mailing lists