Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,15 @@ properties:
- qcom,sm8650-pdc
- qcom,sm8750-pdc
- qcom,x1e80100-pdc
- qcom,x1p42100-pdc
- const: qcom,pdc

reg:
minItems: 1
items:
- description: PDC base register region
- description: Edge or Level config register for SPI interrupts
- description: PDC config for pass through or secondary IRQ mode for GPIOs

'#interrupt-cells':
const: 2
Expand All @@ -81,6 +83,10 @@ properties:
The tuples indicates the valid mapping of valid PDC ports
and their hwirq mapping.

qcom,qmp:
$ref: /schemas/types.yaml#/definitions/phandle
description: Reference to the AOSS side-channel message RAM.

required:
- compatible
- reg
Expand Down
1 change: 1 addition & 0 deletions drivers/irqchip/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ config GOLDFISH_PIC
config QCOM_PDC
tristate "QCOM PDC"
depends on ARCH_QCOM
depends on QCOM_AOSS_QMP
select IRQ_DOMAIN_HIERARCHY
help
Power Domain Controller driver to manage and configure wakeup
Expand Down
119 changes: 110 additions & 9 deletions drivers/irqchip/qcom-pdc.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,32 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/soc/qcom/qcom_aoss.h>

#define PDC_MAX_GPIO_IRQS 256
#define PDC_DRV_OFFSET 0x10000

/* Valid only on HW version < 3.2 */
#define IRQ_ENABLE_BANK 0x10
#define IRQ_ENABLE_BANK_MAX (IRQ_ENABLE_BANK + BITS_TO_BYTES(PDC_MAX_GPIO_IRQS))
#define IRQ_i_CFG_IRQ_MASK_3_0 3
#define IRQ_i_CFG 0x110

/* Valid only on HW version >= 3.2 */
#define IRQ_i_CFG_IRQ_MASK_3_2 4
#define IRQ_i_CFG_IRQ_ENABLE 3

#define IRQ_i_CFG_TYPE_MASK GENMASK(2, 0)

#define PDC_VERSION_REG 0x1000

/* Notable PDC versions */
#define PDC_VERSION_3_0 0x30000
#define PDC_VERSION_3_2 0x30200

#define PDC_PASS_THROUGH_MODE 0

struct pdc_pin_region {
u32 pin_base;
u32 parent_base;
Expand Down Expand Up @@ -97,6 +104,33 @@ static void pdc_x1e_irq_enable_write(u32 bank, u32 enable)
pdc_base_reg_write(base, IRQ_ENABLE_BANK, bank, enable);
}

/*
* The new mask bit controls whether the interrupt is to be forwarded to the
* parent GIC in secondary controller mode. Writing the mask is do not care
* when the PDC is set to pass through mode.
*
* As linux only makes so far make use of pass through mode set all IRQs
* masked during probe.
*/
static void __pdc_mask_intr(int pin_out, bool mask)
{
unsigned long irq_cfg;
int mask_bit;

/* Mask bit available from v3.0 */
if (pdc_version < PDC_VERSION_3_0)
return;

if (pdc_version < PDC_VERSION_3_2)
mask_bit = IRQ_i_CFG_IRQ_MASK_3_0;
else
mask_bit = IRQ_i_CFG_IRQ_MASK_3_2;

irq_cfg = pdc_reg_read(IRQ_i_CFG, pin_out);
__assign_bit(mask_bit, &irq_cfg, mask);
pdc_reg_write(IRQ_i_CFG, pin_out, irq_cfg);
}

static void __pdc_enable_intr(int pin_out, bool on)
{
unsigned long enable;
Expand Down Expand Up @@ -312,7 +346,6 @@ static const struct irq_domain_ops qcom_pdc_ops = {
static int pdc_setup_pin_mapping(struct device_node *np)
{
int ret, n, i;

n = of_property_count_elems_of_size(np, "qcom,pdc-ranges", sizeof(u32));
if (n <= 0 || n % 3)
return -EINVAL;
Expand Down Expand Up @@ -341,8 +374,10 @@ static int pdc_setup_pin_mapping(struct device_node *np)
if (ret)
return ret;

for (i = 0; i < pdc_region[n].cnt; i++)
for (i = 0; i < pdc_region[n].cnt; i++) {
__pdc_enable_intr(i + pdc_region[n].pin_base, 0);
__pdc_mask_intr(i + pdc_region[n].pin_base, true);
}
}

return 0;
Expand All @@ -352,10 +387,13 @@ static int pdc_setup_pin_mapping(struct device_node *np)

static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *parent)
{
static const char buf[64] = "{class: cx_mol, res: cx, val: mol}";
unsigned int domain_flag = IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP;
struct irq_domain *parent_domain, *pdc_domain;
struct device_node *node = pdev->dev.of_node;
resource_size_t res_size;
struct resource res;
struct qmp *pdc_qmp;
int ret;

/* compat with old sm8150 DT which had very small region for PDC */
Expand All @@ -366,6 +404,13 @@ static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *pare
if (res_size > resource_size(&res))
pr_warn("%pOF: invalid reg size, please fix DT\n", node);

pdc_base = ioremap(res.start, res_size);
if (!pdc_base) {
pr_err("%pOF: unable to map PDC registers\n", node);
ret = -ENXIO;
goto fail;
}

/*
* PDC has multiple DRV regions, each one provides the same set of
* registers for a particular client in the system. Due to a hardware
Expand All @@ -382,15 +427,71 @@ static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *pare
}

pdc_x1e_quirk = true;
}

pdc_base = ioremap(res.start, res_size);
if (!pdc_base) {
pr_err("%pOF: unable to map PDC registers\n", node);
ret = -ENXIO;
goto fail;
/*
* There are two modes PDC irqchip can work in
* - pass through mode
* - secondary controller mode
*
* All PDC irqchip supports pass through mode in which both
* Direct SPIs and GPIO IRQs (as SPIs) are sent to GIC
* without latching at PDC.
*
* Newer PDCs (v3.0 onwards) also support additional
* secondary controller mode where PDC latches GPIO IRQs
* and sends to GIC as level type IRQ. Direct SPIs still
* works same as pass through mode without latching at PDC
* even in secondary controller mode.
*
* All the SoCs so far default uses pass through mode with
* the exception of x1e.
*
* x1e modes:
*
* x1e PDC may be set to secondary controller mode for
* builds on CRD boards whereas it may be set to pass
* through mode for IoT-EVK boards.
*
* There is no way to read which current mode it is set to
* and make PDC work in respective mode as the read access
* is not opened up for non secure world. There is though
* write access opened up via SCM write API to set the mode.
*
* Configure PDC mode to pass through mode for all x1e based
* boards.
*
* For successful write:
* - Nothing more to be done
*
* For unsuccessful write:
* - Inform TLMM to monitor GPIO IRQs (same as MPM)
* - Prevent SoC low power mode (CxPC) as PDC is not
* monitoring GPIO IRQs which may be needed to wake
* the SoC from low power mode.
*/
ret = of_address_to_resource(node, 2, &res);
if (ret) {
domain_flag = IRQ_DOMAIN_FLAG_QCOM_MPM_WAKEUP;
goto skip_scm_write;
}

ret = qcom_scm_io_writel(res.start, PDC_PASS_THROUGH_MODE);
if (ret) {
pdc_qmp = qmp_get(&pdev->dev);
if (IS_ERR(pdc_qmp)) {
ret = PTR_ERR(pdc_qmp);
goto fail;
} else {
ret = qmp_send(pdc_qmp, buf, sizeof(buf));
qmp_put(pdc_qmp);
if (ret)
goto fail;
}
domain_flag = IRQ_DOMAIN_FLAG_QCOM_MPM_WAKEUP;
}
}

skip_scm_write:
pdc_version = pdc_reg_read(PDC_VERSION_REG, 0);

parent_domain = irq_find_host(parent);
Expand All @@ -407,7 +508,7 @@ static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *pare
}

pdc_domain = irq_domain_create_hierarchy(parent_domain,
IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP,
domain_flag,
PDC_MAX_GPIO_IRQS,
of_fwnode_handle(node),
&qcom_pdc_ops, NULL);
Expand Down