mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-12 15:49:17 -04:00
irqchip: qcom-pdc: Additionally set type in SPI config registers
GPIOs that can be configured as wakeup are routed to the PDC wakeup interrupt controller and from there to the GIC interrupt controller. On some QCOM SoCs, the interface to the GIC for wakeup capable GPIOs have additional hardware registers that need to be configured as well to match the trigger type of the GPIO. This register interfaces the PDC to the GIC and therefore updated from the PDC driver. Typically, the firmware intializes the interface registers for the wakeup capable GPIOs with commonly used GPIO trigger type, but it is possible that a platform may want to use the GPIO differently. So, in addition to configuring the PDC, configure the interface registers as well. Change-Id: Icab7d9eeb77b5dab9d6a5a8280ce568da40b6ca6 Signed-off-by: Lina Iyer <ilina@codeaurora.org>
This commit is contained in:
parent
67c413154e
commit
1582c6bcd9
2 changed files with 108 additions and 0 deletions
|
|
@ -19,6 +19,8 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/qcom_scm.h>
|
||||
|
||||
#define PDC_MAX_IRQS 168
|
||||
#define PDC_MAX_GPIO_IRQS 256
|
||||
|
||||
|
|
@ -36,10 +38,18 @@ 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_reg_write(int reg, u32 i, u32 val)
|
||||
{
|
||||
|
|
@ -121,6 +131,57 @@ static void qcom_pdc_gic_unmask(struct irq_data *d)
|
|||
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);
|
||||
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 +219,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;
|
||||
|
|
@ -189,6 +252,13 @@ static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type)
|
|||
|
||||
pdc_reg_write(IRQ_i_CFG, pin_out, pdc_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);
|
||||
}
|
||||
|
||||
|
|
@ -377,6 +447,7 @@ static int pdc_setup_pin_mapping(struct device_node *np)
|
|||
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 +478,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 +516,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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue