diff --git a/asoc/codecs/wcd-irq.c b/asoc/codecs/wcd-irq.c new file mode 100644 index 000000000000..12bbf36a5209 --- /dev/null +++ b/asoc/codecs/wcd-irq.c @@ -0,0 +1,191 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int wcd_map_irq(struct wcd_irq_info *irq_info, int irq) +{ + if (!irq_info) { + pr_err("%s: Null IRQ handle\n", __func__); + return -EINVAL; + } + return regmap_irq_get_virq(irq_info->irq_chip, irq); +} + +/** + * wcd_request_irq: Request a thread handler for the given IRQ + * @irq_info: pointer to IRQ info 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 wcd_request_irq(struct wcd_irq_info *irq_info, int irq, const char *name, + irq_handler_t handler, void *data) +{ + if (!irq_info) { + pr_err("%s: Null IRQ handle\n", __func__); + return -EINVAL; + } + irq = wcd_map_irq(irq_info, irq); + if (irq < 0) + return irq; + + return request_threaded_irq(irq, NULL, handler, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + name, data); +} +EXPORT_SYMBOL(wcd_request_irq); + +/** + * wcd_free_irq: Free the IRQ resources allocated during request_irq + * @irq_info: pointer to IRQ info structure + * @irq: irq number + * @data: data pointer + */ +void wcd_free_irq(struct wcd_irq_info *irq_info, int irq, void *data) +{ + if (!irq_info) { + pr_err("%s: Null IRQ handle\n", __func__); + return; + } + + irq = wcd_map_irq(irq_info, irq); + if (irq < 0) + return; + + free_irq(irq, data); +} +EXPORT_SYMBOL(wcd_free_irq); + +/** + * wcd_enable_irq: Enable the given IRQ + * @irq_info: pointer to IRQ info structure + * @irq: irq number + */ +void wcd_enable_irq(struct wcd_irq_info *irq_info, int irq) +{ + if (!irq_info) + pr_err("%s: Null IRQ handle\n", __func__); + else + enable_irq(wcd_map_irq(irq_info, irq)); +} +EXPORT_SYMBOL(wcd_enable_irq); + +/** + * wcd_disable_irq: Disable the given IRQ + * @irq_info: pointer to IRQ info structure + * @irq: irq number + */ +void wcd_disable_irq(struct wcd_irq_info *irq_info, int irq) +{ + if (!irq_info) + pr_err("%s: Null IRQ handle\n", __func__); + else + disable_irq(wcd_map_irq(irq_info, irq)); +} +EXPORT_SYMBOL(wcd_disable_irq); + +static void wcd_irq_chip_disable(struct irq_data *data) +{ +} + +static void wcd_irq_chip_enable(struct irq_data *data) +{ +} + +static struct irq_chip wcd_irq_chip = { + .name = NULL, + .irq_disable = wcd_irq_chip_disable, + .irq_enable = wcd_irq_chip_enable, +}; + +static struct lock_class_key wcd_irq_lock_class; + +static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq); + irq_set_lockdep_class(virq, &wcd_irq_lock_class); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops wcd_domain_ops = { + .map = wcd_irq_chip_map, +}; + +/** + * wcd_irq_init: Initializes IRQ module + * @irq_info: pointer to IRQ info structure + * + * Returns 0 on success or error on failure + */ +int wcd_irq_init(struct wcd_irq_info *irq_info, struct irq_domain **virq) +{ + int ret = 0; + + if (!irq_info) { + pr_err("%s: Null IRQ handle\n", __func__); + return -EINVAL; + } + + wcd_irq_chip.name = irq_info->codec_name; + + *virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL); + if (!(*virq)) { + pr_err("%s: Failed to add IRQ domain\n", __func__); + return -EINVAL; + } + + ret = devm_regmap_add_irq_chip(irq_info->dev, irq_info->regmap, + irq_create_mapping(*virq, 0), + IRQF_ONESHOT, 0, irq_info->wcd_regmap_irq_chip, + &irq_info->irq_chip); + if (ret) + pr_err("%s: Failed to add IRQs: %d\n", + __func__, ret); + + return ret; +} +EXPORT_SYMBOL(wcd_irq_init); + +/** + * wcd_irq_exit: Uninitialize regmap IRQ and free IRQ resources + * @irq_info: pointer to IRQ info structure + * + * Returns 0 on success or error on failure + */ +int wcd_irq_exit(struct wcd_irq_info *irq_info, struct irq_domain *virq) +{ + if (!irq_info) { + pr_err("%s: Null pointer handle\n", __func__); + return -EINVAL; + } + + regmap_del_irq_chip(irq_find_mapping(virq, 0), irq_info->irq_chip); + + return 0; +} +EXPORT_SYMBOL(wcd_irq_exit); diff --git a/include/asoc/wcd-irq.h b/include/asoc/wcd-irq.h new file mode 100644 index 000000000000..63502f828ed8 --- /dev/null +++ b/include/asoc/wcd-irq.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD_IRQ_H_ +#define __WCD_IRQ_H_ + +#include +#include +#include + +struct wcd_irq_info { + struct regmap_irq_chip *wcd_regmap_irq_chip; + char *codec_name; + struct regmap *regmap; + struct regmap_irq_chip_data *irq_chip; + struct device *dev; +}; + +#if IS_ENABLED(CONFIG_WCD9XXX_CODEC_CORE) +int wcd_irq_init(struct wcd_irq_info *irq_info, struct irq_domain **virq); +int wcd_irq_exit(struct wcd_irq_info *irq_info, struct irq_domain *virq); +int wcd_request_irq(struct wcd_irq_info *irq_info, int irq, const char *name, + irq_handler_t handler, void *data); +void wcd_free_irq(struct wcd_irq_info *irq_info, int irq, void *data); +void wcd_enable_irq(struct wcd_irq_info *irq_info, int irq); +void wcd_disable_irq(struct wcd_irq_info *irq_info, int irq); +#else +static inline int wcd_irq_init(struct wcd_irq_info *irq_info, + struct irq_domain **virq) +{ + return 0; +}; +static inline int wcd_irq_exit(struct wcd_irq_info *irq_info, + struct irq_domain *virq) +{ + return 0; +}; +static inline int wcd_request_irq(struct wcd_irq_info *irq, + int irq, const char *name, + irq_handler_t handler, void *data) +{ + return 0; +}; +static inline void wcd_free_irq(struct wcd_irq_info *irq, int irq, void *data); +{ +}; +static inline void wcd_enable_irq(struct wcd_irq_info *irq, int irq); +{ +}; +static inline void wcd_disable_irq(struct wcd_irq_info *irq, int irq); +{ +}; +#endif +#endif /* __WCD_IRQ_H_ */