diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 45dc329a776e..3f8aa534c220 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -374,6 +374,15 @@ config QCOM_WATCHDOG_V2 deadlocks. It does not run during the bootup process, so it will not catch any early lockups. +config QCOM_IRQ_HELPER + bool "QCOM Irq Helper" + help + This enables the irq helper module. It exposes two APIs + int irq_blacklist_on(void) and int irq_blacklist_off(void) + to other kernel module. + These two apis will be used to control the black list used + by the irq balancer. + config QCOM_MEMORY_DUMP bool "Qualcomm Memory Dump Support" help diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index adbf2dc7a166..f8450a4868ad 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o obj-$(CONFIG_QCOM_DCC) += dcc.o obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o obj-$(CONFIG_QCOM_COMMON_LOG) += common_log.o +obj-$(CONFIG_QCOM_IRQ_HELPER) += irq-helper.o obj-$(CONFIG_TRACER_PKT) += tracer_pkt.o obj-$(CONFIG_ICNSS) += icnss.o wlan_firmware_service_v01.o obj-$(CONFIG_SOC_BUS) += socinfo.o diff --git a/drivers/soc/qcom/irq-helper.c b/drivers/soc/qcom/irq-helper.c new file mode 100644 index 000000000000..270a1ba9ba19 --- /dev/null +++ b/drivers/soc/qcom/irq-helper.c @@ -0,0 +1,179 @@ +/* Copyright (c) 2016, 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 + +struct irq_helper { + bool enable; + bool deploy; + uint32_t count; + struct kobject kobj; + /* spinlock to protect reference count variable 'count' */ + spinlock_t lock; +}; + +struct irq_helper_attr { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, struct attribute *attr, + char *buf); + size_t (*store)(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count); +}; + +#define IRQ_HELPER_ATTR(_name, _mode, _show, _store) \ + struct irq_helper_attr irq_helper_##_name = \ + __ATTR(_name, _mode, _show, _store) + +#define to_irq_helper(kobj) \ + container_of(kobj, struct irq_helper, kobj) + +#define to_irq_helper_attr(_attr) \ + container_of(_attr, struct irq_helper_attr, attr) + +static ssize_t attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct irq_helper_attr *irq_attr = to_irq_helper_attr(attr); + ssize_t ret = -EIO; + + if (irq_attr->show) + ret = irq_attr->show(kobj, attr, buf); + + return ret; +} + +static const struct sysfs_ops irq_helper_sysfs_ops = { + .show = attr_show, +}; + +static struct kobj_type irq_helper_ktype = { + .sysfs_ops = &irq_helper_sysfs_ops, +}; + +static ssize_t show_deploy(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct irq_helper *irq = to_irq_helper(kobj); + + return snprintf(buf, sizeof(irq->deploy), "%u\n", irq->deploy); +} +IRQ_HELPER_ATTR(irq_blacklist_on, 0444, show_deploy, NULL); + +static struct irq_helper *irq_h; + +int irq_blacklist_on(void) +{ + bool flag = false; + + if (!irq_h) { + pr_err("%s: init function is not called", __func__); + return -EPERM; + } + if (!irq_h->enable) { + pr_err("%s: enable bit is not set up", __func__); + return -EPERM; + } + spin_lock(&irq_h->lock); + irq_h->count++; + if (!irq_h->deploy) { + irq_h->deploy = true; + flag = true; + } + spin_unlock(&irq_h->lock); + if (flag) + sysfs_notify(&irq_h->kobj, NULL, "irq_blacklist_on"); + return 0; +} +EXPORT_SYMBOL(irq_blacklist_on); + +int irq_blacklist_off(void) +{ + bool flag = false; + + if (!irq_h) { + pr_err("%s: init function is not called", __func__); + return -EPERM; + } + if (!irq_h->enable) { + pr_err("%s: enable bit is not set up", __func__); + return -EPERM; + } + spin_lock(&irq_h->lock); + if (irq_h->count == 0) { + pr_err("%s: ref-count is 0, cannot call irq blacklist off.", + __func__); + spin_unlock(&irq_h->lock); + return -EPERM; + } + irq_h->count--; + if (irq_h->count == 0) { + irq_h->deploy = false; + flag = true; + } + spin_unlock(&irq_h->lock); + + if (flag) + sysfs_notify(&irq_h->kobj, NULL, "irq_blacklist_on"); + return 0; +} +EXPORT_SYMBOL(irq_blacklist_off); + +static int __init irq_helper_init(void) +{ + int ret; + + irq_h = kzalloc(sizeof(struct irq_helper), GFP_KERNEL); + if (!irq_h) + return -ENOMEM; + + ret = kobject_init_and_add(&irq_h->kobj, &irq_helper_ktype, + kernel_kobj, "%s", "irq_helper"); + if (ret) { + pr_err("%s:Error in creation kobject_add\n", __func__); + goto out_free_irq; + } + + ret = sysfs_create_file(&irq_h->kobj, + &irq_helper_irq_blacklist_on.attr); + if (ret) { + pr_err("%s:Error in sysfs_create_file\n", __func__); + goto out_put_kobj; + } + + spin_lock_init(&irq_h->lock); + irq_h->count = 0; + irq_h->enable = true; + return 0; +out_put_kobj: + koject_put(&irq_h->kobj); +out_free_irq: + kfree(irq_h); + return ret; +} +module_init(irq_helper_init); + +static void __exit irq_helper_exit(void) +{ + sysfs_remove_file(&irq_h->kobj, &irq_helper_irq_blacklist_on.attr); + kobject_del(&irq_h->kobj); + kobject_put(&irq_h->kobj); + kfree(irq_h); +} +module_exit(irq_helper_exit); +MODULE_DESCRIPTION("IRQ Helper APIs");