162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2014-2016 IBM Corp.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <misc/cxl-base.h>
862306a36Sopenharmony_ci#include <asm/pnv-pci.h>
962306a36Sopenharmony_ci#include <asm/opal.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "pci.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ciint pnv_phb_to_cxl_mode(struct pci_dev *dev, uint64_t mode)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	struct pci_controller *hose = pci_bus_to_host(dev->bus);
1662306a36Sopenharmony_ci	struct pnv_phb *phb = hose->private_data;
1762306a36Sopenharmony_ci	struct pnv_ioda_pe *pe;
1862306a36Sopenharmony_ci	int rc;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	pe = pnv_ioda_get_pe(dev);
2162306a36Sopenharmony_ci	if (!pe)
2262306a36Sopenharmony_ci		return -ENODEV;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	pe_info(pe, "Switching PHB to CXL\n");
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	rc = opal_pci_set_phb_cxl_mode(phb->opal_id, mode, pe->pe_number);
2762306a36Sopenharmony_ci	if (rc == OPAL_UNSUPPORTED)
2862306a36Sopenharmony_ci		dev_err(&dev->dev, "Required cxl mode not supported by firmware - update skiboot\n");
2962306a36Sopenharmony_ci	else if (rc)
3062306a36Sopenharmony_ci		dev_err(&dev->dev, "opal_pci_set_phb_cxl_mode failed: %i\n", rc);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	return rc;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ciEXPORT_SYMBOL(pnv_phb_to_cxl_mode);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* Find PHB for cxl dev and allocate MSI hwirqs?
3762306a36Sopenharmony_ci * Returns the absolute hardware IRQ number
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_ciint pnv_cxl_alloc_hwirqs(struct pci_dev *dev, int num)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct pci_controller *hose = pci_bus_to_host(dev->bus);
4262306a36Sopenharmony_ci	struct pnv_phb *phb = hose->private_data;
4362306a36Sopenharmony_ci	int hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, num);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (hwirq < 0) {
4662306a36Sopenharmony_ci		dev_warn(&dev->dev, "Failed to find a free MSI\n");
4762306a36Sopenharmony_ci		return -ENOSPC;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return phb->msi_base + hwirq;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_alloc_hwirqs);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_civoid pnv_cxl_release_hwirqs(struct pci_dev *dev, int hwirq, int num)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct pci_controller *hose = pci_bus_to_host(dev->bus);
5762306a36Sopenharmony_ci	struct pnv_phb *phb = hose->private_data;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq - phb->msi_base, num);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_release_hwirqs);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_civoid pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs,
6462306a36Sopenharmony_ci				  struct pci_dev *dev)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct pci_controller *hose = pci_bus_to_host(dev->bus);
6762306a36Sopenharmony_ci	struct pnv_phb *phb = hose->private_data;
6862306a36Sopenharmony_ci	int i, hwirq;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	for (i = 1; i < CXL_IRQ_RANGES; i++) {
7162306a36Sopenharmony_ci		if (!irqs->range[i])
7262306a36Sopenharmony_ci			continue;
7362306a36Sopenharmony_ci		pr_devel("cxl release irq range 0x%x: offset: 0x%lx  limit: %ld\n",
7462306a36Sopenharmony_ci			 i, irqs->offset[i],
7562306a36Sopenharmony_ci			 irqs->range[i]);
7662306a36Sopenharmony_ci		hwirq = irqs->offset[i] - phb->msi_base;
7762306a36Sopenharmony_ci		msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq,
7862306a36Sopenharmony_ci				       irqs->range[i]);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_release_hwirq_ranges);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciint pnv_cxl_alloc_hwirq_ranges(struct cxl_irq_ranges *irqs,
8462306a36Sopenharmony_ci			       struct pci_dev *dev, int num)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct pci_controller *hose = pci_bus_to_host(dev->bus);
8762306a36Sopenharmony_ci	struct pnv_phb *phb = hose->private_data;
8862306a36Sopenharmony_ci	int i, hwirq, try;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	memset(irqs, 0, sizeof(struct cxl_irq_ranges));
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* 0 is reserved for the multiplexed PSL DSI interrupt */
9362306a36Sopenharmony_ci	for (i = 1; i < CXL_IRQ_RANGES && num; i++) {
9462306a36Sopenharmony_ci		try = num;
9562306a36Sopenharmony_ci		while (try) {
9662306a36Sopenharmony_ci			hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, try);
9762306a36Sopenharmony_ci			if (hwirq >= 0)
9862306a36Sopenharmony_ci				break;
9962306a36Sopenharmony_ci			try /= 2;
10062306a36Sopenharmony_ci		}
10162306a36Sopenharmony_ci		if (!try)
10262306a36Sopenharmony_ci			goto fail;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		irqs->offset[i] = phb->msi_base + hwirq;
10562306a36Sopenharmony_ci		irqs->range[i] = try;
10662306a36Sopenharmony_ci		pr_devel("cxl alloc irq range 0x%x: offset: 0x%lx  limit: %li\n",
10762306a36Sopenharmony_ci			 i, irqs->offset[i], irqs->range[i]);
10862306a36Sopenharmony_ci		num -= try;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci	if (num)
11162306a36Sopenharmony_ci		goto fail;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_cifail:
11562306a36Sopenharmony_ci	pnv_cxl_release_hwirq_ranges(irqs, dev);
11662306a36Sopenharmony_ci	return -ENOSPC;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_alloc_hwirq_ranges);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciint pnv_cxl_get_irq_count(struct pci_dev *dev)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct pci_controller *hose = pci_bus_to_host(dev->bus);
12362306a36Sopenharmony_ci	struct pnv_phb *phb = hose->private_data;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return phb->msi_bmp.irq_count;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_get_irq_count);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciint pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq,
13062306a36Sopenharmony_ci			   unsigned int virq)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct pci_controller *hose = pci_bus_to_host(dev->bus);
13362306a36Sopenharmony_ci	struct pnv_phb *phb = hose->private_data;
13462306a36Sopenharmony_ci	unsigned int xive_num = hwirq - phb->msi_base;
13562306a36Sopenharmony_ci	struct pnv_ioda_pe *pe;
13662306a36Sopenharmony_ci	int rc;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (!(pe = pnv_ioda_get_pe(dev)))
13962306a36Sopenharmony_ci		return -ENODEV;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* Assign XIVE to PE */
14262306a36Sopenharmony_ci	rc = opal_pci_set_xive_pe(phb->opal_id, pe->pe_number, xive_num);
14362306a36Sopenharmony_ci	if (rc) {
14462306a36Sopenharmony_ci		pe_warn(pe, "%s: OPAL error %d setting msi_base 0x%x "
14562306a36Sopenharmony_ci			"hwirq 0x%x XIVE 0x%x PE\n",
14662306a36Sopenharmony_ci			pci_name(dev), rc, phb->msi_base, hwirq, xive_num);
14762306a36Sopenharmony_ci		return -EIO;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	pnv_set_msi_irq_chip(phb, virq);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_ioda_msi_setup);
154