Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bbb789f4 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "irqchip: qcom-pdc: Add IPC logging support"

parents d9f38289 47e18613
Loading
Loading
Loading
Loading
+117 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/irqdomain.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
@@ -19,7 +20,11 @@
#include <linux/slab.h>
#include <linux/types.h>

#include <linux/qcom_scm.h>
#include <linux/ipc_logging.h>

#define PDC_MAX_IRQS		168
#define PDC_IPC_LOG_SZ		2
#define PDC_MAX_GPIO_IRQS	256

#define CLEAR_INTR(reg, intr)	(reg & ~(1 << intr))
@@ -36,10 +41,19 @@ struct pdc_pin_region {
	u32 cnt;
};

struct spi_cfg_regs {
	u64 start;
	void __iomem *base;
	resource_size_t size;
	bool scm_io;
};

static DEFINE_RAW_SPINLOCK(pdc_lock);
static void __iomem *pdc_base;
static struct pdc_pin_region *pdc_region;
static int pdc_region_cnt;
static struct spi_cfg_regs *spi_cfg;
static void *pdc_ipc_log;

static void pdc_reg_write(int reg, u32 i, u32 val)
{
@@ -84,6 +98,7 @@ static void pdc_enable_intr(struct irq_data *d, bool on)
	enable = pdc_reg_read(IRQ_ENABLE_BANK, index);
	enable = on ? ENABLE_INTR(enable, mask) : CLEAR_INTR(enable, mask);
	pdc_reg_write(IRQ_ENABLE_BANK, index, enable);
	ipc_log_string(pdc_ipc_log, "PIN=%d enable=%d", d->hwirq, on);
	raw_spin_unlock(&pdc_lock);
}

@@ -110,6 +125,7 @@ static void qcom_pdc_gic_mask(struct irq_data *d)
	if (d->hwirq == GPIO_NO_WAKE_IRQ)
		return;

	ipc_log_string(pdc_ipc_log, "PIN=%d mask", d->hwirq);
	irq_chip_mask_parent(d);
}

@@ -118,9 +134,64 @@ static void qcom_pdc_gic_unmask(struct irq_data *d)
	if (d->hwirq == GPIO_NO_WAKE_IRQ)
		return;

	ipc_log_string(pdc_ipc_log, "PIN=%d unmask", d->hwirq);
	irq_chip_unmask_parent(d);
}

static u32 __spi_pin_read(unsigned int pin)
{
	void __iomem *cfg_reg = spi_cfg->base + pin * 4;
	u64 scm_cfg_reg = spi_cfg->start + pin * 4;

	if (spi_cfg->scm_io) {
		unsigned int val;

		qcom_scm_io_readl(scm_cfg_reg, &val);
		return val;
	} else {
		return readl_relaxed(cfg_reg);
	}
}

static void __spi_pin_write(unsigned int pin, unsigned int val)
{
	void __iomem *cfg_reg = spi_cfg->base + pin * 4;
	u64 scm_cfg_reg = spi_cfg->start + pin * 4;

	if (spi_cfg->scm_io)
		qcom_scm_io_writel(scm_cfg_reg, val);
	else
		writel_relaxed(val, cfg_reg);
}

static int spi_configure_type(irq_hw_number_t hwirq, unsigned int type)
{
	int spi = hwirq - 32;
	u32 pin = spi / 32;
	u32 mask = BIT(spi % 32);
	u32 val;
	unsigned long flags;

	if (!spi_cfg)
		return 0;

	if (pin * 4 > spi_cfg->size)
		return -EFAULT;

	raw_spin_lock_irqsave(&pdc_lock, flags);
	val = __spi_pin_read(pin);
	val &= ~mask;
	if (type & IRQ_TYPE_LEVEL_MASK)
		val |= mask;
	__spi_pin_write(pin, val);
	ipc_log_string(pdc_ipc_log,
		       "SPI config: GIC-SPI=%d (reg=%d,bit=%d) val=%d",
		       spi, pin, spi % 32, type & IRQ_TYPE_LEVEL_MASK);
	raw_spin_unlock_irqrestore(&pdc_lock, flags);

	return 0;
}

/*
 * GIC does not handle falling edge or active low. To allow falling edge and
 * active low interrupts to be handled at GIC, PDC has an inverter that inverts
@@ -158,7 +229,9 @@ enum pdc_irq_config_bits {
static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type)
{
	int pin_out = d->hwirq;
	int parent_hwirq = d->parent_data->hwirq;
	enum pdc_irq_config_bits pdc_type;
	int ret;

	if (pin_out == GPIO_NO_WAKE_IRQ)
		return 0;
@@ -188,6 +261,15 @@ static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type)
	}

	pdc_reg_write(IRQ_i_CFG, pin_out, pdc_type);
	ipc_log_string(pdc_ipc_log, "Set type: PIN=%d pdc_type=%d gic_type=%d",
		       pin_out, pdc_type, type);

	/* Additionally, configure (only) the GPIO in the f/w */
	if (irq_domain_qcom_handle_wakeup(d->domain)) {
		ret = spi_configure_type(parent_hwirq, type);
		if (ret)
			return ret;
	}

	return irq_chip_set_type_parent(d, type);
}
@@ -274,6 +356,8 @@ static int qcom_pdc_alloc(struct irq_domain *domain, unsigned int virq,
	parent_fwspec.param[1]    = parent_hwirq;
	parent_fwspec.param[2]    = type;

	ipc_log_string(pdc_ipc_log, "Alloc: PIN=%d GIC-SPI=%d",
		       hwirq, parent_hwirq);
	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
					    &parent_fwspec);
}
@@ -321,6 +405,8 @@ static int qcom_pdc_gpio_alloc(struct irq_domain *domain, unsigned int virq,
	parent_fwspec.param[1]    = parent_hwirq;
	parent_fwspec.param[2]    = type;

	ipc_log_string(pdc_ipc_log, "GPIO alloc: PIN=%d GIC-SPI=%d",
		       hwirq, parent_hwirq);
	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
					    &parent_fwspec);
}
@@ -374,9 +460,18 @@ static int pdc_setup_pin_mapping(struct device_node *np)
	return 0;
}

static int __init qcom_pdc_early_init(void)
{
	pdc_ipc_log = ipc_log_context_create(PDC_IPC_LOG_SZ, "pdc", 0);

	return 0;
}
module_init(qcom_pdc_early_init);

static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
{
	struct irq_domain *parent_domain, *pdc_domain, *pdc_gpio_domain;
	struct resource res;
	int ret;

	pdc_base = of_iomap(node, 0);
@@ -407,6 +502,27 @@ static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
		goto fail;
	}

	ret = of_address_to_resource(node, 1, &res);
	if (!ret) {
		spi_cfg = kcalloc(1, sizeof(*spi_cfg), GFP_KERNEL);
		if (!spi_cfg) {
			ret = -ENOMEM;
			goto remove;
		}
		spi_cfg->scm_io = of_find_property(node,
						   "qcom,scm-spi-cfg", NULL);
		spi_cfg->size = resource_size(&res);
		if (spi_cfg->scm_io) {
			spi_cfg->start = res.start;
		} else {
			spi_cfg->base = ioremap(res.start, spi_cfg->size);
			if (!spi_cfg->base) {
				ret = -ENOMEM;
				goto remove;
			}
		}
	}

	pdc_gpio_domain = irq_domain_create_hierarchy(parent_domain,
					IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP,
					PDC_MAX_GPIO_IRQS,
@@ -424,6 +540,7 @@ static int qcom_pdc_init(struct device_node *node, struct device_node *parent)

remove:
	irq_domain_remove(pdc_domain);
	kfree(spi_cfg);
fail:
	kfree(pdc_region);
	iounmap(pdc_base);
+15 −0
Original line number Diff line number Diff line
@@ -3,6 +3,8 @@
#ifndef __QCOM_IRQ_H
#define __QCOM_IRQ_H

struct irq_domain;

#define GPIO_NO_WAKE_IRQ	~0U

/**
@@ -16,4 +18,17 @@
#define IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP		(IRQ_DOMAIN_FLAG_NONCORE << 0)
#define IRQ_DOMAIN_FLAG_QCOM_MPM_WAKEUP		(IRQ_DOMAIN_FLAG_NONCORE << 1)

/**
 * irq_domain_qcom_handle_wakeup: Return if the domain handles interrupt
 *                                configuration
 * @d: irq domain
 *
 * This QCOM specific irq domain call returns if the interrupt controller
 * requires the interrupt be masked at the child interrupt controller.
 */
static inline bool irq_domain_qcom_handle_wakeup(struct irq_domain *d)
{
	return (d->flags & IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP);
}

#endif