162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Freescale MU used as MSI controller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2018 Pengutronix, Oleksij Rempel <o.rempel@pengutronix.de>
662306a36Sopenharmony_ci * Copyright 2022 NXP
762306a36Sopenharmony_ci *	Frank Li <Frank.Li@nxp.com>
862306a36Sopenharmony_ci *	Peng Fan <peng.fan@nxp.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Based on drivers/mailbox/imx-mailbox.c
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/clk.h>
1462306a36Sopenharmony_ci#include <linux/irq.h>
1562306a36Sopenharmony_ci#include <linux/irqchip.h>
1662306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
1762306a36Sopenharmony_ci#include <linux/irqdomain.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/msi.h>
2162306a36Sopenharmony_ci#include <linux/of_irq.h>
2262306a36Sopenharmony_ci#include <linux/of_platform.h>
2362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2462306a36Sopenharmony_ci#include <linux/pm_domain.h>
2562306a36Sopenharmony_ci#include <linux/spinlock.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define IMX_MU_CHANS            4
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cienum imx_mu_xcr {
3062306a36Sopenharmony_ci	IMX_MU_GIER,
3162306a36Sopenharmony_ci	IMX_MU_GCR,
3262306a36Sopenharmony_ci	IMX_MU_TCR,
3362306a36Sopenharmony_ci	IMX_MU_RCR,
3462306a36Sopenharmony_ci	IMX_MU_xCR_MAX,
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cienum imx_mu_xsr {
3862306a36Sopenharmony_ci	IMX_MU_SR,
3962306a36Sopenharmony_ci	IMX_MU_GSR,
4062306a36Sopenharmony_ci	IMX_MU_TSR,
4162306a36Sopenharmony_ci	IMX_MU_RSR,
4262306a36Sopenharmony_ci	IMX_MU_xSR_MAX
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cienum imx_mu_type {
4662306a36Sopenharmony_ci	IMX_MU_V2 = BIT(1),
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Receive Interrupt Enable */
5062306a36Sopenharmony_ci#define IMX_MU_xCR_RIEn(data, x) ((data->cfg->type) & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
5162306a36Sopenharmony_ci#define IMX_MU_xSR_RFn(data, x) ((data->cfg->type) & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistruct imx_mu_dcfg {
5462306a36Sopenharmony_ci	enum imx_mu_type type;
5562306a36Sopenharmony_ci	u32     xTR;            /* Transmit Register0 */
5662306a36Sopenharmony_ci	u32     xRR;            /* Receive Register0 */
5762306a36Sopenharmony_ci	u32     xSR[IMX_MU_xSR_MAX];         /* Status Registers */
5862306a36Sopenharmony_ci	u32     xCR[IMX_MU_xCR_MAX];         /* Control Registers */
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct imx_mu_msi {
6262306a36Sopenharmony_ci	raw_spinlock_t			lock;
6362306a36Sopenharmony_ci	struct irq_domain		*msi_domain;
6462306a36Sopenharmony_ci	void __iomem			*regs;
6562306a36Sopenharmony_ci	phys_addr_t			msiir_addr;
6662306a36Sopenharmony_ci	const struct imx_mu_dcfg	*cfg;
6762306a36Sopenharmony_ci	unsigned long			used;
6862306a36Sopenharmony_ci	struct clk			*clk;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void imx_mu_write(struct imx_mu_msi *msi_data, u32 val, u32 offs)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	iowrite32(val, msi_data->regs + offs);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic u32 imx_mu_read(struct imx_mu_msi *msi_data, u32 offs)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	return ioread32(msi_data->regs + offs);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic u32 imx_mu_xcr_rmw(struct imx_mu_msi *msi_data, enum imx_mu_xcr type, u32 set, u32 clr)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	unsigned long flags;
8462306a36Sopenharmony_ci	u32 val;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	raw_spin_lock_irqsave(&msi_data->lock, flags);
8762306a36Sopenharmony_ci	val = imx_mu_read(msi_data, msi_data->cfg->xCR[type]);
8862306a36Sopenharmony_ci	val &= ~clr;
8962306a36Sopenharmony_ci	val |= set;
9062306a36Sopenharmony_ci	imx_mu_write(msi_data, val, msi_data->cfg->xCR[type]);
9162306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&msi_data->lock, flags);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return val;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void imx_mu_msi_parent_mask_irq(struct irq_data *data)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	imx_mu_xcr_rmw(msi_data, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(msi_data, data->hwirq));
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void imx_mu_msi_parent_unmask_irq(struct irq_data *data)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	imx_mu_xcr_rmw(msi_data, IMX_MU_RCR, IMX_MU_xCR_RIEn(msi_data, data->hwirq), 0);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void imx_mu_msi_parent_ack_irq(struct irq_data *data)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	imx_mu_read(msi_data, msi_data->cfg->xRR + data->hwirq * 4);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct irq_chip imx_mu_msi_irq_chip = {
11862306a36Sopenharmony_ci	.name = "MU-MSI",
11962306a36Sopenharmony_ci	.irq_ack = irq_chip_ack_parent,
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic struct msi_domain_ops imx_mu_msi_irq_ops = {
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic struct msi_domain_info imx_mu_msi_domain_info = {
12662306a36Sopenharmony_ci	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
12762306a36Sopenharmony_ci	.ops	= &imx_mu_msi_irq_ops,
12862306a36Sopenharmony_ci	.chip	= &imx_mu_msi_irq_chip,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void imx_mu_msi_parent_compose_msg(struct irq_data *data,
13262306a36Sopenharmony_ci					  struct msi_msg *msg)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data);
13562306a36Sopenharmony_ci	u64 addr = msi_data->msiir_addr + 4 * data->hwirq;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	msg->address_hi = upper_32_bits(addr);
13862306a36Sopenharmony_ci	msg->address_lo = lower_32_bits(addr);
13962306a36Sopenharmony_ci	msg->data = data->hwirq;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int imx_mu_msi_parent_set_affinity(struct irq_data *irq_data,
14362306a36Sopenharmony_ci				   const struct cpumask *mask, bool force)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	return -EINVAL;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic struct irq_chip imx_mu_msi_parent_chip = {
14962306a36Sopenharmony_ci	.name		= "MU",
15062306a36Sopenharmony_ci	.irq_mask	= imx_mu_msi_parent_mask_irq,
15162306a36Sopenharmony_ci	.irq_unmask	= imx_mu_msi_parent_unmask_irq,
15262306a36Sopenharmony_ci	.irq_ack	= imx_mu_msi_parent_ack_irq,
15362306a36Sopenharmony_ci	.irq_compose_msi_msg	= imx_mu_msi_parent_compose_msg,
15462306a36Sopenharmony_ci	.irq_set_affinity = imx_mu_msi_parent_set_affinity,
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int imx_mu_msi_domain_irq_alloc(struct irq_domain *domain,
15862306a36Sopenharmony_ci					unsigned int virq,
15962306a36Sopenharmony_ci					unsigned int nr_irqs,
16062306a36Sopenharmony_ci					void *args)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct imx_mu_msi *msi_data = domain->host_data;
16362306a36Sopenharmony_ci	unsigned long flags;
16462306a36Sopenharmony_ci	int pos, err = 0;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	WARN_ON(nr_irqs != 1);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	raw_spin_lock_irqsave(&msi_data->lock, flags);
16962306a36Sopenharmony_ci	pos = find_first_zero_bit(&msi_data->used, IMX_MU_CHANS);
17062306a36Sopenharmony_ci	if (pos < IMX_MU_CHANS)
17162306a36Sopenharmony_ci		__set_bit(pos, &msi_data->used);
17262306a36Sopenharmony_ci	else
17362306a36Sopenharmony_ci		err = -ENOSPC;
17462306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&msi_data->lock, flags);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (err)
17762306a36Sopenharmony_ci		return err;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	irq_domain_set_info(domain, virq, pos,
18062306a36Sopenharmony_ci			    &imx_mu_msi_parent_chip, msi_data,
18162306a36Sopenharmony_ci			    handle_edge_irq, NULL, NULL);
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic void imx_mu_msi_domain_irq_free(struct irq_domain *domain,
18662306a36Sopenharmony_ci				       unsigned int virq, unsigned int nr_irqs)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
18962306a36Sopenharmony_ci	struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(d);
19062306a36Sopenharmony_ci	unsigned long flags;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	raw_spin_lock_irqsave(&msi_data->lock, flags);
19362306a36Sopenharmony_ci	__clear_bit(d->hwirq, &msi_data->used);
19462306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&msi_data->lock, flags);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic const struct irq_domain_ops imx_mu_msi_domain_ops = {
19862306a36Sopenharmony_ci	.alloc	= imx_mu_msi_domain_irq_alloc,
19962306a36Sopenharmony_ci	.free	= imx_mu_msi_domain_irq_free,
20062306a36Sopenharmony_ci};
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void imx_mu_msi_irq_handler(struct irq_desc *desc)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct imx_mu_msi *msi_data = irq_desc_get_handler_data(desc);
20562306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
20662306a36Sopenharmony_ci	u32 status;
20762306a36Sopenharmony_ci	int i;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	status = imx_mu_read(msi_data, msi_data->cfg->xSR[IMX_MU_RSR]);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
21262306a36Sopenharmony_ci	for (i = 0; i < IMX_MU_CHANS; i++) {
21362306a36Sopenharmony_ci		if (status & IMX_MU_xSR_RFn(msi_data, i))
21462306a36Sopenharmony_ci			generic_handle_domain_irq(msi_data->msi_domain, i);
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int imx_mu_msi_domains_init(struct imx_mu_msi *msi_data, struct device *dev)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct fwnode_handle *fwnodes = dev_fwnode(dev);
22262306a36Sopenharmony_ci	struct irq_domain *parent;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* Initialize MSI domain parent */
22562306a36Sopenharmony_ci	parent = irq_domain_create_linear(fwnodes,
22662306a36Sopenharmony_ci					    IMX_MU_CHANS,
22762306a36Sopenharmony_ci					    &imx_mu_msi_domain_ops,
22862306a36Sopenharmony_ci					    msi_data);
22962306a36Sopenharmony_ci	if (!parent) {
23062306a36Sopenharmony_ci		dev_err(dev, "failed to create IRQ domain\n");
23162306a36Sopenharmony_ci		return -ENOMEM;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	msi_data->msi_domain = platform_msi_create_irq_domain(fwnodes,
23762306a36Sopenharmony_ci					&imx_mu_msi_domain_info,
23862306a36Sopenharmony_ci					parent);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (!msi_data->msi_domain) {
24162306a36Sopenharmony_ci		dev_err(dev, "failed to create MSI domain\n");
24262306a36Sopenharmony_ci		irq_domain_remove(parent);
24362306a36Sopenharmony_ci		return -ENOMEM;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	irq_domain_set_pm_device(msi_data->msi_domain, dev);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/* Register offset of different version MU IP */
25262306a36Sopenharmony_cistatic const struct imx_mu_dcfg imx_mu_cfg_imx6sx = {
25362306a36Sopenharmony_ci	.type	= 0,
25462306a36Sopenharmony_ci	.xTR    = 0x0,
25562306a36Sopenharmony_ci	.xRR    = 0x10,
25662306a36Sopenharmony_ci	.xSR    = {
25762306a36Sopenharmony_ci			[IMX_MU_SR]  = 0x20,
25862306a36Sopenharmony_ci			[IMX_MU_GSR] = 0x20,
25962306a36Sopenharmony_ci			[IMX_MU_TSR] = 0x20,
26062306a36Sopenharmony_ci			[IMX_MU_RSR] = 0x20,
26162306a36Sopenharmony_ci		  },
26262306a36Sopenharmony_ci	.xCR    = {
26362306a36Sopenharmony_ci			[IMX_MU_GIER] = 0x24,
26462306a36Sopenharmony_ci			[IMX_MU_GCR]  = 0x24,
26562306a36Sopenharmony_ci			[IMX_MU_TCR]  = 0x24,
26662306a36Sopenharmony_ci			[IMX_MU_RCR]  = 0x24,
26762306a36Sopenharmony_ci		  },
26862306a36Sopenharmony_ci};
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = {
27162306a36Sopenharmony_ci	.type	= 0,
27262306a36Sopenharmony_ci	.xTR    = 0x20,
27362306a36Sopenharmony_ci	.xRR    = 0x40,
27462306a36Sopenharmony_ci	.xSR    = {
27562306a36Sopenharmony_ci			[IMX_MU_SR]  = 0x60,
27662306a36Sopenharmony_ci			[IMX_MU_GSR] = 0x60,
27762306a36Sopenharmony_ci			[IMX_MU_TSR] = 0x60,
27862306a36Sopenharmony_ci			[IMX_MU_RSR] = 0x60,
27962306a36Sopenharmony_ci		  },
28062306a36Sopenharmony_ci	.xCR    = {
28162306a36Sopenharmony_ci			[IMX_MU_GIER] = 0x64,
28262306a36Sopenharmony_ci			[IMX_MU_GCR]  = 0x64,
28362306a36Sopenharmony_ci			[IMX_MU_TCR]  = 0x64,
28462306a36Sopenharmony_ci			[IMX_MU_RCR]  = 0x64,
28562306a36Sopenharmony_ci		  },
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
28962306a36Sopenharmony_ci	.type   = IMX_MU_V2,
29062306a36Sopenharmony_ci	.xTR    = 0x200,
29162306a36Sopenharmony_ci	.xRR    = 0x280,
29262306a36Sopenharmony_ci	.xSR    = {
29362306a36Sopenharmony_ci			[IMX_MU_SR]  = 0xC,
29462306a36Sopenharmony_ci			[IMX_MU_GSR] = 0x118,
29562306a36Sopenharmony_ci			[IMX_MU_TSR] = 0x124,
29662306a36Sopenharmony_ci			[IMX_MU_RSR] = 0x12C,
29762306a36Sopenharmony_ci		  },
29862306a36Sopenharmony_ci	.xCR    = {
29962306a36Sopenharmony_ci			[IMX_MU_GIER] = 0x110,
30062306a36Sopenharmony_ci			[IMX_MU_GCR]  = 0x114,
30162306a36Sopenharmony_ci			[IMX_MU_TCR]  = 0x120,
30262306a36Sopenharmony_ci			[IMX_MU_RCR]  = 0x128
30362306a36Sopenharmony_ci		  },
30462306a36Sopenharmony_ci};
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic int __init imx_mu_of_init(struct device_node *dn,
30762306a36Sopenharmony_ci				 struct device_node *parent,
30862306a36Sopenharmony_ci				 const struct imx_mu_dcfg *cfg)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct platform_device *pdev = of_find_device_by_node(dn);
31162306a36Sopenharmony_ci	struct device_link *pd_link_a;
31262306a36Sopenharmony_ci	struct device_link *pd_link_b;
31362306a36Sopenharmony_ci	struct imx_mu_msi *msi_data;
31462306a36Sopenharmony_ci	struct resource *res;
31562306a36Sopenharmony_ci	struct device *pd_a;
31662306a36Sopenharmony_ci	struct device *pd_b;
31762306a36Sopenharmony_ci	struct device *dev;
31862306a36Sopenharmony_ci	int ret;
31962306a36Sopenharmony_ci	int irq;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	dev = &pdev->dev;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
32462306a36Sopenharmony_ci	if (!msi_data)
32562306a36Sopenharmony_ci		return -ENOMEM;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	msi_data->cfg = cfg;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	msi_data->regs = devm_platform_ioremap_resource_byname(pdev, "processor-a-side");
33062306a36Sopenharmony_ci	if (IS_ERR(msi_data->regs)) {
33162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to initialize 'regs'\n");
33262306a36Sopenharmony_ci		return PTR_ERR(msi_data->regs);
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "processor-b-side");
33662306a36Sopenharmony_ci	if (!res)
33762306a36Sopenharmony_ci		return -EIO;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	msi_data->msiir_addr = res->start + msi_data->cfg->xTR;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
34262306a36Sopenharmony_ci	if (irq < 0)
34362306a36Sopenharmony_ci		return irq;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	platform_set_drvdata(pdev, msi_data);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	msi_data->clk = devm_clk_get(dev, NULL);
34862306a36Sopenharmony_ci	if (IS_ERR(msi_data->clk))
34962306a36Sopenharmony_ci		return PTR_ERR(msi_data->clk);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	pd_a = dev_pm_domain_attach_by_name(dev, "processor-a-side");
35262306a36Sopenharmony_ci	if (IS_ERR(pd_a))
35362306a36Sopenharmony_ci		return PTR_ERR(pd_a);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	pd_b = dev_pm_domain_attach_by_name(dev, "processor-b-side");
35662306a36Sopenharmony_ci	if (IS_ERR(pd_b))
35762306a36Sopenharmony_ci		return PTR_ERR(pd_b);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	pd_link_a = device_link_add(dev, pd_a,
36062306a36Sopenharmony_ci			DL_FLAG_STATELESS |
36162306a36Sopenharmony_ci			DL_FLAG_PM_RUNTIME |
36262306a36Sopenharmony_ci			DL_FLAG_RPM_ACTIVE);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (!pd_link_a) {
36562306a36Sopenharmony_ci		dev_err(dev, "Failed to add device_link to mu a.\n");
36662306a36Sopenharmony_ci		goto err_pd_a;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	pd_link_b = device_link_add(dev, pd_b,
37062306a36Sopenharmony_ci			DL_FLAG_STATELESS |
37162306a36Sopenharmony_ci			DL_FLAG_PM_RUNTIME |
37262306a36Sopenharmony_ci			DL_FLAG_RPM_ACTIVE);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (!pd_link_b) {
37662306a36Sopenharmony_ci		dev_err(dev, "Failed to add device_link to mu a.\n");
37762306a36Sopenharmony_ci		goto err_pd_b;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	ret = imx_mu_msi_domains_init(msi_data, dev);
38162306a36Sopenharmony_ci	if (ret)
38262306a36Sopenharmony_ci		goto err_dm_init;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	pm_runtime_enable(dev);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	irq_set_chained_handler_and_data(irq,
38762306a36Sopenharmony_ci					 imx_mu_msi_irq_handler,
38862306a36Sopenharmony_ci					 msi_data);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return 0;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cierr_dm_init:
39362306a36Sopenharmony_ci	device_link_remove(dev,	pd_b);
39462306a36Sopenharmony_cierr_pd_b:
39562306a36Sopenharmony_ci	device_link_remove(dev, pd_a);
39662306a36Sopenharmony_cierr_pd_a:
39762306a36Sopenharmony_ci	return -EINVAL;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int __maybe_unused imx_mu_runtime_suspend(struct device *dev)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct imx_mu_msi *priv = dev_get_drvdata(dev);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	clk_disable_unprepare(priv->clk);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic int __maybe_unused imx_mu_runtime_resume(struct device *dev)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct imx_mu_msi *priv = dev_get_drvdata(dev);
41262306a36Sopenharmony_ci	int ret;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ret = clk_prepare_enable(priv->clk);
41562306a36Sopenharmony_ci	if (ret)
41662306a36Sopenharmony_ci		dev_err(dev, "failed to enable clock\n");
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	return ret;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic const struct dev_pm_ops imx_mu_pm_ops = {
42262306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend,
42362306a36Sopenharmony_ci			   imx_mu_runtime_resume, NULL)
42462306a36Sopenharmony_ci};
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int __init imx_mu_imx7ulp_of_init(struct device_node *dn,
42762306a36Sopenharmony_ci					 struct device_node *parent)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx7ulp);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic int __init imx_mu_imx6sx_of_init(struct device_node *dn,
43362306a36Sopenharmony_ci					struct device_node *parent)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx6sx);
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic int __init imx_mu_imx8ulp_of_init(struct device_node *dn,
43962306a36Sopenharmony_ci					 struct device_node *parent)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx8ulp);
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ciIRQCHIP_PLATFORM_DRIVER_BEGIN(imx_mu_msi)
44562306a36Sopenharmony_ciIRQCHIP_MATCH("fsl,imx7ulp-mu-msi", imx_mu_imx7ulp_of_init)
44662306a36Sopenharmony_ciIRQCHIP_MATCH("fsl,imx6sx-mu-msi", imx_mu_imx6sx_of_init)
44762306a36Sopenharmony_ciIRQCHIP_MATCH("fsl,imx8ulp-mu-msi", imx_mu_imx8ulp_of_init)
44862306a36Sopenharmony_ciIRQCHIP_PLATFORM_DRIVER_END(imx_mu_msi, .pm = &imx_mu_pm_ops)
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ciMODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
45262306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale MU MSI controller driver");
45362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
454