diff --git a/asoc/codecs/aqt1000/aqt1000-core.c b/asoc/codecs/aqt1000/aqt1000-core.c index 2306a2acfd7e..f6fee2063a09 100644 --- a/asoc/codecs/aqt1000/aqt1000-core.c +++ b/asoc/codecs/aqt1000/aqt1000-core.c @@ -28,6 +28,7 @@ #include "aqt1000-internal.h" #include "aqt1000.h" #include "aqt1000-utils.h" +#include "aqt1000-irq.h" static int aqt1000_bringup(struct aqt1000 *aqt) { @@ -113,6 +114,11 @@ static int aqt1000_device_init(struct aqt1000 *aqt) goto done; } + ret = aqt_irq_init(aqt); + if (ret) + goto done; + + return ret; done: mutex_destroy(&aqt->io_lock); mutex_destroy(&aqt->xfer_lock); @@ -392,6 +398,7 @@ static int aqt1000_bringdown(struct device *dev) static void aqt1000_device_exit(struct aqt1000 *aqt) { + aqt_irq_exit(aqt); aqt1000_bringdown(aqt->dev); mutex_destroy(&aqt->io_lock); mutex_destroy(&aqt->xfer_lock); diff --git a/asoc/codecs/aqt1000/aqt1000-irq.c b/asoc/codecs/aqt1000/aqt1000-irq.c index e01dc4602379..0c1335306465 100644 --- a/asoc/codecs/aqt1000/aqt1000-irq.c +++ b/asoc/codecs/aqt1000/aqt1000-irq.c @@ -12,174 +12,289 @@ #include #include -#include #include #include +#include #include #include #include #include #include #include +#include + +#include "pdata.h" +#include "aqt1000.h" #include "aqt1000-registers.h" #include "aqt1000-irq.h" +static const struct regmap_irq aqt1000_irqs[AQT1000_NUM_IRQS] = { + REGMAP_IRQ_REG(AQT1000_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x01), + REGMAP_IRQ_REG(AQT1000_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x02), + REGMAP_IRQ_REG(AQT1000_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04), + REGMAP_IRQ_REG(AQT1000_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08), + REGMAP_IRQ_REG(AQT1000_IRQ_MBHC_SW_DET, 0, 0x10), + REGMAP_IRQ_REG(AQT1000_IRQ_HPH_PA_OCPL_FAULT, 0, 0x20), + REGMAP_IRQ_REG(AQT1000_IRQ_HPH_PA_OCPR_FAULT, 0, 0x40), + REGMAP_IRQ_REG(AQT1000_IRQ_HPH_PA_CNPL_COMPLETE, 0, 0x80), + REGMAP_IRQ_REG(AQT1000_IRQ_HPH_PA_CNPR_COMPLETE, 1, 0x01), + REGMAP_IRQ_REG(AQT1000_CDC_HPHL_SURGE, 1, 0x02), + REGMAP_IRQ_REG(AQT1000_CDC_HPHR_SURGE, 1, 0x04), +}; -static int aqt_irq_init(struct aqt1000_irq *aqt_irq) +static const struct regmap_irq_chip aqt_regmap_irq_chip = { + .name = "AQT1000", + .irqs = aqt1000_irqs, + .num_irqs = ARRAY_SIZE(aqt1000_irqs), + .num_regs = 2, + .status_base = AQT1000_INTR_CTRL_INT_STATUS_2, + .mask_base = AQT1000_INTR_CTRL_INT_MASK_2, + .unmask_base = AQT1000_INTR_CTRL_INT_CLEAR_2, + .ack_base = AQT1000_INTR_CTRL_INT_STATUS_2, + .runtime_pm = true, +}; + +static int aqt_map_irq(struct aqt1000 *aqt, int irq) { - int i, ret; - - if (aqt_irq == NULL) { - pr_err("%s: aqt_irq is NULL\n", __func__); - return -EINVAL; - } - mutex_init(&aqt_irq->irq_lock); - mutex_init(&aqt_irq->nested_irq_lock); - - aqt_irq->irq = aqt_irq_get_upstream_irq(aqt_irq); - if (!aqt_irq->irq) { - pr_warn("%s: irq driver is not yet initialized\n", __func__); - mutex_destroy(&aqt_irq->irq_lock); - mutex_destroy(&aqt_irq->nested_irq_lock); - return -EPROBE_DEFER; - } - pr_debug("%s: probed irq %d\n", __func__, aqt_irq->irq); - - /* Setup downstream IRQs */ - ret = aqt_irq_setup_downstream_irq(aqt_irq); - if (ret) { - pr_err("%s: Failed to setup downstream IRQ\n", __func__); - goto fail_irq_init; - } - - /* mask all the interrupts */ - for (i = 0; i < aqt_irq->num_irqs; i++) { - aqt_irq->irq_masks_cur |= BYTE_BIT_MASK(i); - aqt_irq->irq_masks_cache |= BYTE_BIT_MASK(i); - } - - ret = request_threaded_irq(aqt_irq->irq, NULL, aqt_irq_thread, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - "aqt", aqt_irq); - if (ret != 0) { - dev_err(aqt_irq->dev, "Failed to request IRQ %d: %d\n", - aqt_irq->irq, ret); - } else { - ret = enable_irq_wake(aqt_irq->irq); - if (ret) { - dev_err(aqt_irq->dev, - "Failed to set wake interrupt on IRQ %d: %d\n", - aqt_irq->irq, ret); - free_irq(aqt_irq->irq, aqt_irq); - } - } - - if (ret) - goto fail_irq_init; - - return ret; - -fail_irq_init: - dev_err(aqt_irq->dev, - "%s: Failed to init aqt irq\n", __func__); - aqt_irq_put_upstream_irq(aqt_irq); - mutex_destroy(&aqt_irq->irq_lock); - mutex_destroy(&aqt_irq->nested_irq_lock); - return ret; + return regmap_irq_get_virq(aqt->irq_chip, irq); } -static int aqt_irq_probe(struct platform_device *pdev) +/** + * aqt_request_irq: Request a thread handler for the given IRQ + * @aqt: pointer to aqt1000 structure + * @irq: irq number + * @name: name for the IRQ thread + * @handler: irq handler + * @data: data pointer + * + * Returns 0 on success or error on failure + */ +int aqt_request_irq(struct aqt1000 *aqt, int irq, const char *name, + irq_handler_t handler, void *data) { - int irq; - struct aqt1000_irq *aqt_irq = NULL; - int ret = -EINVAL; + irq = aqt_map_irq(aqt, irq); + if (irq < 0) + return irq; - irq = platform_get_irq_byname(pdev, "aqt-int"); - if (irq < 0) { - dev_err(&pdev->dev, "%s: Couldn't find aqt-int node(%d)\n", - __func__, irq); - return -EINVAL; - } - aqt_irq = kzalloc(sizeof(*aqt_irq), GFP_KERNEL); - if (!aqt_irq) - return -ENOMEM; - /* - * AQT interrupt controller supports N to N irq mapping with - * single cell binding with irq numbers(offsets) only. - * Use irq_domain_simple_ops that has irq_domain_simple_map and - * irq_domain_xlate_onetwocell. - */ - aqt_irq->dev = &pdev->dev; - aqt_irq->domain = irq_domain_add_linear(aqt_irq->dev->of_node, - WSA_NUM_IRQS, &irq_domain_simple_ops, - aqt_irq); - if (!aqt_irq->domain) { - dev_err(&pdev->dev, "%s: domain is NULL\n", __func__); - ret = -ENOMEM; - goto err; - } - aqt_irq->dev = &pdev->dev; + return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, + name, data); +} +EXPORT_SYMBOL(aqt_request_irq); - dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq); - aqt_irq->irq = irq; - aqt_irq->num_irq_regs = 2; - aqt_irq->num_irqs = WSA_NUM_IRQS; - ret = aqt_irq_init(aqt_irq); +/** + * aqt_free_irq: Free the IRQ resources allocated during request_irq + * @aqt: pointer to aqt1000 structure + * @irq: irq number + * @data: data pointer + */ +void aqt_free_irq(struct aqt1000 *aqt, int irq, void *data) +{ + irq = aqt_map_irq(aqt, irq); + if (irq < 0) + return; + + free_irq(irq, data); +} +EXPORT_SYMBOL(aqt_free_irq); + +/** + * aqt_enable_irq: Enable the given IRQ + * @aqt: pointer to aqt1000 structure + * @irq: irq number + */ +void aqt_enable_irq(struct aqt1000 *aqt, int irq) +{ + if (aqt) + enable_irq(aqt_map_irq(aqt, irq)); +} +EXPORT_SYMBOL(aqt_enable_irq); + +/** + * aqt_disable_irq: Disable the given IRQ + * @aqt: pointer to aqt1000 structure + * @irq: irq number + */ +void aqt_disable_irq(struct aqt1000 *aqt, int irq) +{ + if (aqt) + disable_irq(aqt_map_irq(aqt, irq)); +} +EXPORT_SYMBOL(aqt_disable_irq); + +static irqreturn_t aqt_irq_thread(int irq, void *data) +{ + int ret = 0; + u8 sts[2]; + struct aqt1000 *aqt = data; + int num_irq_regs = aqt->num_irq_regs; + struct aqt1000_pdata *pdata; + + pdata = dev_get_platdata(aqt->dev); + + memset(sts, 0, sizeof(sts)); + ret = regmap_bulk_read(aqt->regmap, AQT1000_INTR_CTRL_INT_STATUS_2, + sts, num_irq_regs); if (ret < 0) { - dev_err(&pdev->dev, "%s: failed to do irq init %d\n", - __func__, ret); - goto err; + dev_err(aqt->dev, "%s: Failed to read intr status: %d\n", + __func__, ret); + } else if (ret == 0) { + while (gpio_get_value_cansleep(pdata->irq_gpio)) + handle_nested_irq(irq_find_mapping(aqt->virq, 0)); } - return ret; -err: - kfree(aqt_irq); - return ret; + return IRQ_HANDLED; } -static int aqt_irq_remove(struct platform_device *pdev) +static void aqt_irq_disable(struct irq_data *data) { - struct irq_domain *domain; - struct aqt1000_irq *data; +} - domain = irq_find_host(pdev->dev.of_node); - if (unlikely(!domain)) { - pr_err("%s: domain is NULL\n", __func__); - return -EINVAL; - } - data = (struct aqt_irq *)domain->host_data; - data->irq = 0; +static void aqt_irq_enable(struct irq_data *data) +{ +} + +static struct irq_chip aqt_irq_chip = { + .name = "AQT", + .irq_disable = aqt_irq_disable, + .irq_enable = aqt_irq_enable, +}; + +static int aqt_irq_map(struct irq_domain *irqd, unsigned int virq, + irq_hw_number_t hw) +{ + struct aqt1000 *data = irqd->host_data; + + irq_set_chip_data(virq, data); + irq_set_chip_and_handler(virq, &aqt_irq_chip, handle_simple_irq); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); return 0; } -static const struct of_device_id of_match[] = { - { .compatible = "qcom,aqt-irq" }, - { } +static const struct irq_domain_ops aqt_domain_ops = { + .map = aqt_irq_map, + .xlate = irq_domain_xlate_twocell, }; -static struct platform_driver aqt_irq_driver = { - .probe = aqt_irq_probe, - .remove = aqt_irq_remove, - .driver = { - .name = "aqt_intc", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(of_match), - }, -}; - -static int aqt_irq_drv_init(void) +/** + * aqt_irq_init: Initializes IRQ module + * @aqt: pointer to aqt1000 structure + * + * Returns 0 on success or error on failure + */ +int aqt_irq_init(struct aqt1000 *aqt) { - return platform_driver_register(&aqt_irq_driver); -} -subsys_initcall(aqt_irq_drv_init); + int i, ret; + unsigned int flags = IRQF_ONESHOT; + struct irq_data *irq_data; + struct aqt1000_pdata *pdata; -static void aqt_irq_drv_exit(void) + if (!aqt) { + pr_err("%s: Null pointer handle\n", __func__); + return -EINVAL; + } + + if (!aqt->irq) { + dev_dbg(aqt->dev, "%s: No interrupt specified\n", __func__); + aqt->irq_base = 0; + return 0; + } + + pdata = dev_get_platdata(aqt->dev); + if (!pdata) { + dev_err(aqt->dev, "%s: Invalid platform data\n", __func__); + return -EINVAL; + } + + /* Select default if not defined in DT */ + flags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; + if (pdata->irq_flags) + flags = pdata->irq_flags; + + if (pdata->irq_gpio) { + if (gpio_to_irq(pdata->irq_gpio) != aqt->irq) { + dev_warn(aqt->dev, "%s: IRQ %d is not GPIO %d (%d)\n", + __func__, aqt->irq, pdata->irq_gpio, + gpio_to_irq(pdata->irq_gpio)); + aqt->irq = gpio_to_irq(pdata->irq_gpio); + } + + ret = devm_gpio_request_one(aqt->dev, pdata->irq_gpio, + GPIOF_IN, "AQT IRQ"); + if (ret) { + dev_err(aqt->dev, "%s: Failed to request gpio %d\n", + __func__, ret); + pdata->irq_gpio = 0; + return ret; + } + } else { + dev_dbg(aqt->dev, "%s: irq_gpio is %d\n", + __func__, pdata->irq_gpio); + return 0; + } + + irq_data = irq_get_irq_data(aqt->irq); + if (!irq_data) { + dev_err(aqt->dev, "%s: Invalid IRQ: %d\n", + __func__, aqt->irq); + return -EINVAL; + } + + for (i = 0; i < aqt->num_irq_regs; i++) { + regmap_write(aqt->regmap, + (AQT1000_INTR_CTRL_INT_TYPE_2 + i), 0); + } + + aqt->virq = irq_domain_add_linear(NULL, 1, &aqt_domain_ops, aqt); + if (!aqt->virq) { + dev_err(aqt->dev, "%s: Failed to add IRQ domain\n", __func__); + ret = -EINVAL; + goto err; + } + ret = regmap_add_irq_chip(aqt->regmap, + irq_create_mapping(aqt->virq, 0), + IRQF_ONESHOT, 0, &aqt_regmap_irq_chip, + &aqt->irq_chip); + if (ret) { + dev_err(aqt->dev, "%s: Failed to add IRQs: %d\n", + __func__, ret); + goto err; + } + + ret = request_threaded_irq(aqt->irq, NULL, aqt_irq_thread, flags, + "aqt", aqt); + if (ret) { + dev_err(aqt->dev, "%s: failed to register irq: %d\n", + __func__, ret); + goto err_irq; + } + + return 0; + +err_irq: + regmap_del_irq_chip(irq_create_mapping(aqt->virq, 1), aqt->irq_chip); +err: + return ret; +} +EXPORT_SYMBOL(aqt_irq_init); + +/** + * aqt_irq_exit: Uninitialize regmap IRQ and free IRQ resources + * @aqt: pointer to aqt1000 structure + * + * Returns 0 on success or error on failure + */ +int aqt_irq_exit(struct aqt1000 *aqt) { - platform_driver_unregister(&aqt_irq_driver); -} -module_exit(aqt_irq_drv_exit); + if (!aqt) { + pr_err("%s: Null pointer handle\n", __func__); + return -EINVAL; + } + regmap_del_irq_chip(irq_create_mapping(aqt->virq, 1), aqt->irq_chip); + free_irq(aqt->irq, aqt); -MODULE_DESCRIPTION("AQT1000 IRQ driver"); -MODULE_LICENSE("GPL v2"); + return 0; +} +EXPORT_SYMBOL(aqt_irq_exit); diff --git a/asoc/codecs/aqt1000/aqt1000-irq.h b/asoc/codecs/aqt1000/aqt1000-irq.h index dd5b1166a19d..60be49c21163 100644 --- a/asoc/codecs/aqt1000/aqt1000-irq.h +++ b/asoc/codecs/aqt1000/aqt1000-irq.h @@ -13,6 +13,7 @@ #ifndef __AQT1000_IRQ_H_ #define __AQT1000_IRQ_H_ +#include #include #include @@ -31,43 +32,15 @@ enum { AQT1000_IRQ_HPH_PA_CNPR_COMPLETE, AQT1000_CDC_HPHL_SURGE, AQT1000_CDC_HPHR_SURGE, - AQT1000_PLL_LOCK_LOSS, - AQT1000_FLL_LOCK_LOSS, - AQT1000_DSD_INT, AQT1000_NUM_IRQS, }; -/** - * struct aqt_irq - AQT IRQ resource structure - * @irq_lock: lock used by irq_chip functions. - * @nested_irq_lock: lock used while handling nested interrupts. - * @irq: interrupt number. - * @irq_masks_cur: current mask value to be written to mask registers. - * @irq_masks_cache: cached mask value. - * @num_irqs: number of supported interrupts. - * @num_irq_regs: number of irq registers. - * @parent: parent pointer. - * @dev: device pointer. - * @domain: irq domain pointer. - * - * Contains required members used in wsa irq driver. - */ - -struct aqt1000_irq { - struct mutex irq_lock; - struct mutex nested_irq_lock; - unsigned int irq; - u8 irq_masks_cur; - u8 irq_masks_cache; - bool irq_level_high[8]; - int num_irqs; - int num_irq_regs; - void *parent; - struct device *dev; - struct irq_domain *domain; -}; - -int aqt_irq_init(void); -void aqt_irq_exit(void); +int aqt_request_irq(struct aqt1000 *aqt, int irq, const char *name, + irq_handler_t handler, void *data); +void aqt_free_irq(struct aqt1000 *aqt, int irq, void *data); +int aqt_irq_init(struct aqt1000 *aqt); +int aqt_irq_exit(struct aqt1000 *aqt); +void aqt_enable_irq(struct aqt1000 *aqt, int irq); +void aqt_disable_irq(struct aqt1000 *aqt, int irq); #endif /* __AQT1000_IRQ_H_ */