// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2018-22 Raspberry Pi Ltd.
 * All rights reserved.
 */

#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/completion.h>
#include <linux/etherdevice.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/mfd/core.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/rp1_platform.h>
#include <linux/reset.h>
#include <linux/slab.h>

#include <dt-bindings/mfd/rp1.h>

/* TO DO:
 * 1. Occasional shutdown crash - RP1 being closed before its children?
 * 2. DT mode interrupt handling.
 */

#define RP1_DRIVER_NAME "rp1"

#define PCI_VENDOR_ID_RPI 0x1de4
#define PCI_DEVICE_ID_RP1_C0 0x0001
#define PCI_DEVICE_REV_RP1_C0 2

#define RP1_ACTUAL_IRQS		RP1_INT_END
#define RP1_IRQS		RP1_ACTUAL_IRQS

#define RP1_SYSCLK_RATE		200000000
#define RP1_SYSCLK_FPGA_RATE	60000000

// Don't want to include the whole sysinfo reg header
#define SYSINFO_CHIP_ID_OFFSET	0x00000000
#define SYSINFO_PLATFORM_OFFSET	0x00000004

#define REG_RW          0x000
#define REG_SET         0x800
#define REG_CLR         0xc00

// MSIX CFG registers start at 0x8
#define MSIX_CFG(x) (0x8 + (4 * (x)))

#define MSIX_CFG_IACK_EN        BIT(3)
#define MSIX_CFG_IACK           BIT(2)
#define MSIX_CFG_TEST           BIT(1)
#define MSIX_CFG_ENABLE         BIT(0)

#define INTSTATL		0x108
#define INTSTATH		0x10c

struct rp1_dev {
	struct pci_dev *pdev;
	struct device *dev;
	resource_size_t bar_start;
	resource_size_t bar_end;
	struct clk *sys_clk;
	struct irq_domain *domain;
	struct irq_data *pcie_irqds[64];
	void __iomem *msix_cfg_regs;
};

static bool rp1_level_triggered_irq[RP1_ACTUAL_IRQS] = { 0 };

static struct rp1_dev *g_rp1;
static u32 g_chip_id, g_platform;

static void dump_bar(struct pci_dev *pdev, unsigned int bar)
{
	dev_info(&pdev->dev,
		 "bar%d len 0x%llx, start 0x%llx, end 0x%llx, flags, 0x%lx\n",
		 bar,
		 pci_resource_len(pdev, bar),
		 pci_resource_start(pdev, bar),
		 pci_resource_end(pdev, bar),
		 pci_resource_flags(pdev, bar));
}

static void msix_cfg_set(struct rp1_dev *rp1, unsigned int hwirq, u32 value)
{
	writel(value, rp1->msix_cfg_regs + REG_SET + MSIX_CFG(hwirq));
}

static void msix_cfg_clr(struct rp1_dev *rp1, unsigned int hwirq, u32 value)
{
	writel(value, rp1->msix_cfg_regs + REG_CLR + MSIX_CFG(hwirq));
}

static void rp1_mask_irq(struct irq_data *irqd)
{
	struct rp1_dev *rp1 = irqd->domain->host_data;
	struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq];

	pci_msi_mask_irq(pcie_irqd);
}

static void rp1_unmask_irq(struct irq_data *irqd)
{
	struct rp1_dev *rp1 = irqd->domain->host_data;
	struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq];

	pci_msi_unmask_irq(pcie_irqd);
}

static int rp1_irq_set_type(struct irq_data *irqd, unsigned int type)
{
	struct rp1_dev *rp1 = irqd->domain->host_data;
	unsigned int hwirq = (unsigned int)irqd->hwirq;
	int ret = 0;

	switch (type) {
	case IRQ_TYPE_LEVEL_HIGH:
		dev_dbg(rp1->dev, "MSIX IACK EN for irq %d\n", hwirq);
		msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK_EN);
		rp1_level_triggered_irq[hwirq] = true;
	break;
	case IRQ_TYPE_EDGE_RISING:
		msix_cfg_clr(rp1, hwirq, MSIX_CFG_IACK_EN);
		rp1_level_triggered_irq[hwirq] = false;
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret;
}

static int rp1_irq_set_affinity(struct irq_data *irqd, const struct cpumask *dest, bool force)
{
	struct rp1_dev *rp1 = irqd->domain->host_data;
	struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq];

	return msi_domain_set_affinity(pcie_irqd, dest, force);
}

static struct irq_chip rp1_irq_chip = {
	.name            = "rp1_irq_chip",
	.irq_mask        = rp1_mask_irq,
	.irq_unmask      = rp1_unmask_irq,
	.irq_set_type    = rp1_irq_set_type,
	.irq_set_affinity = rp1_irq_set_affinity,
};

static void rp1_chained_handle_irq(struct irq_desc *desc)
{
	struct irq_chip *chip = irq_desc_get_chip(desc);
	struct rp1_dev *rp1 = desc->irq_data.chip_data;
	unsigned int hwirq = desc->irq_data.hwirq & 0x3f;
	int new_irq;

	rp1 = g_rp1;

	chained_irq_enter(chip, desc);

	new_irq = irq_linear_revmap(rp1->domain, hwirq);
	generic_handle_irq(new_irq);
	if (rp1_level_triggered_irq[hwirq])
		msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK);

	chained_irq_exit(chip, desc);
}

static int rp1_irq_xlate(struct irq_domain *d, struct device_node *node,
			 const u32 *intspec, unsigned int intsize,
			 unsigned long *out_hwirq, unsigned int *out_type)
{
	struct rp1_dev *rp1 = d->host_data;
	struct irq_data *pcie_irqd;
	unsigned long hwirq;
	int pcie_irq;
	int ret;

	ret = irq_domain_xlate_twocell(d, node, intspec, intsize,
				       &hwirq, out_type);
	if (!ret) {
		pcie_irq = pci_irq_vector(rp1->pdev, hwirq);
		pcie_irqd = irq_get_irq_data(pcie_irq);
		rp1->pcie_irqds[hwirq] = pcie_irqd;
		*out_hwirq = hwirq;
	}
	return ret;
}

static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd,
			    bool reserve)
{
	struct rp1_dev *rp1 = d->host_data;
	struct irq_data *pcie_irqd;

	pcie_irqd = rp1->pcie_irqds[irqd->hwirq];
	msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE);
	return irq_domain_activate_irq(pcie_irqd, reserve);
}

static void rp1_irq_deactivate(struct irq_domain *d, struct irq_data *irqd)
{
	struct rp1_dev *rp1 = d->host_data;
	struct irq_data *pcie_irqd;

	pcie_irqd = rp1->pcie_irqds[irqd->hwirq];
	msix_cfg_clr(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE);
	return irq_domain_deactivate_irq(pcie_irqd);
}

static const struct irq_domain_ops rp1_domain_ops = {
	.xlate      = rp1_irq_xlate,
	.activate   = rp1_irq_activate,
	.deactivate = rp1_irq_deactivate,
};

static inline dma_addr_t rp1_io_to_phys(struct rp1_dev *rp1, unsigned int offset)
{
	return rp1->bar_start + offset;
}

static u32 rp1_reg_read(struct rp1_dev *rp1, unsigned int base_addr, u32 offset)
{
	dma_addr_t phys = rp1_io_to_phys(rp1, base_addr);
	void __iomem *regblock = ioremap(phys, 0x1000);
	u32 value = readl(regblock + offset);

	iounmap(regblock);
	return value;
}

void rp1_get_platform(u32 *chip_id, u32 *platform)
{
	if (chip_id)
		*chip_id = g_chip_id;
	if (platform)
		*platform = g_platform;
}
EXPORT_SYMBOL_GPL(rp1_get_platform);

static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	struct reset_control *reset;
	struct platform_device *pcie_pdev;
	struct device_node *rp1_node;
	struct rp1_dev *rp1;
	int err  = 0;
	int i;

	reset = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
	if (IS_ERR(reset))
		return PTR_ERR(reset);
	reset_control_reset(reset);

	dump_bar(pdev, 0);
	dump_bar(pdev, 1);

	if (pci_resource_len(pdev, 1) <= 0x10000) {
		dev_err(&pdev->dev,
			"Not initialised - is the firmware running?\n");
		return -EINVAL;
	}

	/* enable pci device */
	err = pcim_enable_device(pdev);
	if (err < 0) {
		dev_err(&pdev->dev, "Enabling PCI device has failed: %d",
			err);
		return err;
	}

	pci_set_master(pdev);

	err = pci_alloc_irq_vectors(pdev, RP1_IRQS, RP1_IRQS,
				    PCI_IRQ_MSIX);
	if (err != RP1_IRQS) {
		dev_err(&pdev->dev, "pci_alloc_irq_vectors failed - %d\n", err);
		return err;
	}

	rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL);
	if (!rp1)
		return -ENOMEM;

	rp1->pdev = pdev;
	rp1->dev = &pdev->dev;

	pci_set_drvdata(pdev, rp1);

	rp1->bar_start = pci_resource_start(pdev, 1);
	rp1->bar_end = pci_resource_end(pdev, 1);

	// Get chip id
	g_chip_id = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_CHIP_ID_OFFSET);
	g_platform = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_PLATFORM_OFFSET);
	dev_info(&pdev->dev, "chip_id 0x%x%s\n", g_chip_id,
		 (g_platform & RP1_PLATFORM_FPGA) ? " FPGA" : "");
	if (g_chip_id != RP1_C0_CHIP_ID) {
		dev_err(&pdev->dev, "wrong chip id (%x)\n", g_chip_id);
		return -EINVAL;
	}

	rp1_node = of_find_node_by_name(NULL, "rp1");
	if (!rp1_node) {
		dev_err(&pdev->dev, "failed to find RP1 DT node\n");
		return -EINVAL;
	}

	pcie_pdev = of_find_device_by_node(rp1_node->parent);
	rp1->domain = irq_domain_add_linear(rp1_node, RP1_IRQS,
					    &rp1_domain_ops, rp1);

	g_rp1 = rp1;

	/* TODO can this go in the rp1 device tree entry? */
	rp1->msix_cfg_regs = ioremap(rp1_io_to_phys(rp1, RP1_PCIE_APBS_BASE), 0x1000);

	for (i = 0; i < RP1_IRQS; i++) {
		int irq = irq_create_mapping(rp1->domain, i);

		if (irq < 0) {
			dev_err(&pdev->dev, "failed to create irq mapping\n");
			return irq;
		}

		irq_set_chip_data(irq, rp1);
		irq_set_chip_and_handler(irq, &rp1_irq_chip, handle_level_irq);
		irq_set_probe(irq);
		irq_set_chained_handler(pci_irq_vector(pdev, i),
					rp1_chained_handle_irq);
	}

	of_node_to_fwnode(rp1_node)->dev = rp1->dev;
	of_platform_populate(rp1_node, NULL, NULL, &pcie_pdev->dev);
	of_node_put(rp1_node);

	return 0;
}

static void rp1_remove(struct pci_dev *pdev)
{
	struct rp1_dev *rp1 = pci_get_drvdata(pdev);

	mfd_remove_devices(&pdev->dev);

	clk_unregister(rp1->sys_clk);
}

static const struct pci_device_id dev_id_table[] = {
	{ PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RP1_C0), },
	{ 0, }
};

static struct pci_driver rp1_driver = {
	.name		= RP1_DRIVER_NAME,
	.id_table	= dev_id_table,
	.probe		= rp1_probe,
	.remove		= rp1_remove,
};

module_pci_driver(rp1_driver);

MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
MODULE_DESCRIPTION("RP1 wrapper");
MODULE_LICENSE("GPL");
