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