Skip to content

Commit

Permalink
ARM: mstar: msc313 pm intc driver
Browse files Browse the repository at this point in the history
This adds a driver for the PM interrupt *controller* found in
MStar/SigmaStar ARMv7 SoCs.

The PM parts of these chips is an always on domain that runs
even when the ARM CPU is powered down so that decoding an IR
command or similar can wake the ARM CPU up and boot the main
OS. It seems this part of the chip contains an 8051
micro-controller but I haven't been able to confirm that.

Controller is really a stretch here as it's basically a single
register that collects the interrupt status for other blocks in
the PM area, like the PM GPIO, that have their own interrupt
mask/clear/type bits.

The main function seems to be OR'ing together the interrupts
from the PM blocks to trigger a single interrupt that is wired
to the non-PM part of the chip so that hardware in the PM side
can be used when the ARM CPU is running.

In this driver when the upstream interrupt fires the status
register is used to trigger the right interrupt with PM.
The driver that is consuming the interrupt is responsible
for actually handling it.

Signed-off-by: Daniel Palmer <daniel@0x0f.com>
  • Loading branch information
fifteenhex committed Jul 11, 2021
1 parent b662d52 commit 5ef6fea
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 0 deletions.
8 changes: 8 additions & 0 deletions drivers/irqchip/Kconfig
Expand Up @@ -601,4 +601,12 @@ config APPLE_AIC
Support for the Apple Interrupt Controller found on Apple Silicon SoCs,
such as the M1.

config MSC313_PM_INTC
bool "MStar MSC313 Interrupt Controller"
depends on ARCH_MSTARV7 || COMPILE_TEST
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
help
Support MStar Interrupt Controller.

endmenu
2 changes: 2 additions & 0 deletions drivers/irqchip/Makefile
Expand Up @@ -116,3 +116,5 @@ obj-$(CONFIG_MACH_REALTEK_RTL) += irq-realtek-rtl.o
obj-$(CONFIG_WPCM450_AIC) += irq-wpcm450-aic.o
obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o
obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o
obj-$(CONFIG_MSC313_PM_INTC) += irq-msc313-pm-intc.o

132 changes: 132 additions & 0 deletions drivers/irqchip/irq-msc313-pm-intc.c
@@ -0,0 +1,132 @@
// 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 32
#define REG_STATUS 0x0

struct msc313_sleep_intc {
struct regmap *pmsleep;
};

static void msc313_sleep_intc_mask_irq(struct irq_data *data)
{
}

static void msc313_sleep_intc_unmask_irq(struct irq_data *data)
{
}

static void msc313_sleep_intc_irq_eoi(struct irq_data *data)
{
}

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

static struct irq_chip msc313_pm_intc_chip = {
.name = "PM-INTC",
.irq_mask = msc313_sleep_intc_mask_irq,
.irq_unmask = msc313_sleep_intc_unmask_irq,
.irq_eoi = msc313_sleep_intc_irq_eoi,
.irq_set_type = msc313_sleep_intc_set_type_irq,
};

static int msc313_sleep_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_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_intc_domain_ops = {
.xlate = irq_domain_xlate_twocell,
.map = msc313_sleep_intc_domain_map,
};

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

regmap_read(intc->pmsleep, MSTAR_PMSLEEP_INTSTATUS + 4, &tmp);
status = tmp << 16;
regmap_read(intc->pmsleep, MSTAR_PMSLEEP_INTSTATUS, &tmp);
status |= tmp;

while (status) {
hwirq = __ffs(status);
virq = irq_find_mapping(domain, hwirq);
if (virq)
generic_handle_irq(virq);
status &= ~BIT(hwirq);
}

return IRQ_HANDLED;
}

static int __init msc313_sleep_intc_of_init(struct device_node *node,
struct device_node *parent)
{
int gicint;
struct regmap *pmsleep;
struct msc313_sleep_intc *intc;
struct irq_domain *domain;
int ret;

gicint = of_irq_get(node, 0);
printk("gicint: %d\n", gicint);
if (gicint <= 0)
return gicint;

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->pmsleep = pmsleep;

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

request_irq(gicint, msc313_sleep_intc_chainedhandler, IRQF_SHARED,
"pmsleep", domain);

return 0;

out_free:
kfree(intc);
return ret;
}

IRQCHIP_DECLARE(mstar_msc313_sleep_intc, "mstar,msc313-pm-intc",
msc313_sleep_intc_of_init);

0 comments on commit 5ef6fea

Please sign in to comment.