/* 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_nosync(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);