Skip to content
Permalink
Browse files
irqchip: MStar wakeup intc
All of the known MStar/SigmaStar ARMv7 SoCs have a set of
interrupts that can wake the chip from deep sleep.

The status bits of these are kept in the PM sleep region.
When the interrupt triggers the ARM core is woken up and
it sees an interrupt on the pm sleep interrupt.

Signed-off-by: Daniel Palmer <daniel@0x0f.com>
  • Loading branch information
fifteenhex committed Dec 13, 2021
1 parent c6bc1bc commit ef6b5b20ff3e51fa6026bc510f66980c5107698c
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 1 deletion.
@@ -2299,6 +2299,7 @@ F: arch/arm/mach-mstar/
F: drivers/clk/mstar/
F: drivers/gpio/gpio-msc313.c
F: drivers/irqchip/irq-msc313-pm-intc.c
F: drivers/irqchip/irq-msc313-pm-wakeup.c
F: drivers/irqchip/irq-ssd20xd-gpi.c
F: drivers/pinctrl/mstar/
F: drivers/rtc/rtc-msc313.c
@@ -118,4 +118,4 @@ obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o
obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o
obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o
obj-$(CONFIG_SSD20XD_GPI) += irq-ssd20xd-gpi.o
obj-$(CONFIG_MSC313_PM_INTC) += irq-msc313-pm-intc.o
obj-$(CONFIG_MSC313_PM_INTC) += irq-msc313-pm-intc.o irq-msc313-pm-wakeup.o
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 Daniel Palmer
*/

#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/interrupt.h>

#include <soc/mstar/pmsleep.h>

#define NUM_IRQ 8

static struct reg_field field_mask = REG_FIELD(MSTAR_PMSLEEP_WAKEUPSOURCE, 0, 7);
static struct reg_field field_type = REG_FIELD(MSTAR_PMSLEEP_REG24, 0, 7);
static struct reg_field field_status = REG_FIELD(MSTAR_PMSLEEP_WAKEINT_STATUS, 0, 7);

struct msc313_sleep_intc {
struct regmap_field *mask;
struct regmap_field *type;
struct regmap_field *status;
};

static void msc313_pm_wakeup_intc_mask_irq(struct irq_data *data)
{
struct msc313_sleep_intc *intc = irq_data_get_irq_chip_data(data);

regmap_field_update_bits(intc->mask, 1 << data->hwirq, ~0);
}

static void msc313_pm_wakeup_intc_unmask_irq(struct irq_data *data)
{
struct msc313_sleep_intc *intc = irq_data_get_irq_chip_data(data);

regmap_field_update_bits(intc->mask, 1 << data->hwirq, 0);
}

static void msc313_pm_wakeup_intc_irq_eoi(struct irq_data *data)
{
}

static int msc313_pm_wakeup_intc_set_type_irq(struct irq_data *data,
unsigned int flow_type)
{
return 0;
}

static struct irq_chip msc313_pm_wakeup_intc_chip = {
.name = "PM-WAKEUP",
.irq_mask = msc313_pm_wakeup_intc_mask_irq,
.irq_unmask = msc313_pm_wakeup_intc_unmask_irq,
.irq_eoi = msc313_pm_wakeup_intc_irq_eoi,
.irq_set_type = msc313_pm_wakeup_intc_set_type_irq,
};

static irqreturn_t msc313_pm_wakeup_intc_chainedhandler(int irq, void *data)
{
struct irq_domain *domain = data;
struct msc313_sleep_intc *intc = domain->host_data;
unsigned int hwirq, status;

regmap_field_read(intc->status, &status);
printk("wakeupint %x\n", status);

while (status) {
hwirq = __ffs(status);
generic_handle_domain_irq(domain, hwirq);
status &= ~BIT(hwirq);
}

return IRQ_HANDLED;
}

static int msc313_pm_wakeup_intc_domain_map(struct irq_domain *domain,
unsigned int irq, irq_hw_number_t hw)
{
struct msc313_sleep_intc *intc = domain->host_data;

irq_set_chip_and_handler(irq, &msc313_pm_wakeup_intc_chip, handle_level_irq);
irq_set_chip_data(irq, intc);
irq_set_probe(irq);

return 0;
}

static const struct irq_domain_ops msc313_pm_wakeup_intc_domain_ops = {
.xlate = irq_domain_xlate_twocell,
.map = msc313_pm_wakeup_intc_domain_map,
};

static int __init msc313_pm_wakeup_intc_of_init(struct device_node *node,
struct device_node *parent)
{
int ret = 0, irq;
struct regmap *pmsleep;
struct msc313_sleep_intc *intc;
struct irq_domain *domain;

irq = of_irq_get(node, 0);
if (irq <= 0)
return irq;

pmsleep = syscon_regmap_lookup_by_phandle(node, "mstar,pmsleep");
if (IS_ERR(pmsleep))
return PTR_ERR(pmsleep);

intc = kzalloc(sizeof(*intc), GFP_KERNEL);
if (!intc)
return -ENOMEM;

intc->mask = regmap_field_alloc(pmsleep, field_mask);
intc->type = regmap_field_alloc(pmsleep, field_type);
intc->status = regmap_field_alloc(pmsleep, field_status);

/* The masks survive deep sleep so clear them. */
regmap_field_write(intc->mask, ~0);

domain = irq_domain_add_linear(node, NUM_IRQ,
&msc313_pm_wakeup_intc_domain_ops, intc);
if (!domain) {
ret = -ENOMEM;
goto out_free;
}

request_irq(irq, msc313_pm_wakeup_intc_chainedhandler, IRQF_SHARED,
"pmsleep", domain);

return 0;

out_free:
kfree(intc);
return ret;
}

IRQCHIP_DECLARE(mstar_msc313_pm_wakeup_intc, "mstar,msc313-pm-wakeup-intc",
msc313_pm_wakeup_intc_of_init);

0 comments on commit ef6b5b2

Please sign in to comment.