162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2021, MediaTek Inc.
462306a36Sopenharmony_ci * Copyright (c) 2021-2022, Intel Corporation.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Authors:
762306a36Sopenharmony_ci *  Haijun Liu <haijun.liu@mediatek.com>
862306a36Sopenharmony_ci *  Moises Veleta <moises.veleta@intel.com>
962306a36Sopenharmony_ci *  Sreehari Kancharla <sreehari.kancharla@intel.com>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Contributors:
1262306a36Sopenharmony_ci *  Amir Hanania <amir.hanania@intel.com>
1362306a36Sopenharmony_ci *  Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com>
1462306a36Sopenharmony_ci *  Ricardo Martinez <ricardo.martinez@linux.intel.com>
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/bits.h>
1862306a36Sopenharmony_ci#include <linux/bitops.h>
1962306a36Sopenharmony_ci#include <linux/device.h>
2062306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h>
2162306a36Sopenharmony_ci#include <linux/pci.h>
2262306a36Sopenharmony_ci#include <linux/string.h>
2362306a36Sopenharmony_ci#include <linux/types.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "t7xx_pci.h"
2662306a36Sopenharmony_ci#include "t7xx_pcie_mac.h"
2762306a36Sopenharmony_ci#include "t7xx_reg.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define T7XX_PCIE_REG_BAR		2
3062306a36Sopenharmony_ci#define T7XX_PCIE_REG_PORT		ATR_SRC_PCI_WIN0
3162306a36Sopenharmony_ci#define T7XX_PCIE_REG_TABLE_NUM		0
3262306a36Sopenharmony_ci#define T7XX_PCIE_REG_TRSL_PORT		ATR_DST_AXIM_0
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define T7XX_PCIE_DEV_DMA_PORT_START	ATR_SRC_AXIS_0
3562306a36Sopenharmony_ci#define T7XX_PCIE_DEV_DMA_PORT_END	ATR_SRC_AXIS_2
3662306a36Sopenharmony_ci#define T7XX_PCIE_DEV_DMA_TABLE_NUM	0
3762306a36Sopenharmony_ci#define T7XX_PCIE_DEV_DMA_TRSL_ADDR	0
3862306a36Sopenharmony_ci#define T7XX_PCIE_DEV_DMA_SRC_ADDR	0
3962306a36Sopenharmony_ci#define T7XX_PCIE_DEV_DMA_TRANSPARENT	1
4062306a36Sopenharmony_ci#define T7XX_PCIE_DEV_DMA_SIZE		0
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cienum t7xx_atr_src_port {
4362306a36Sopenharmony_ci	ATR_SRC_PCI_WIN0,
4462306a36Sopenharmony_ci	ATR_SRC_PCI_WIN1,
4562306a36Sopenharmony_ci	ATR_SRC_AXIS_0,
4662306a36Sopenharmony_ci	ATR_SRC_AXIS_1,
4762306a36Sopenharmony_ci	ATR_SRC_AXIS_2,
4862306a36Sopenharmony_ci	ATR_SRC_AXIS_3,
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cienum t7xx_atr_dst_port {
5262306a36Sopenharmony_ci	ATR_DST_PCI_TRX,
5362306a36Sopenharmony_ci	ATR_DST_PCI_CONFIG,
5462306a36Sopenharmony_ci	ATR_DST_AXIM_0 = 4,
5562306a36Sopenharmony_ci	ATR_DST_AXIM_1,
5662306a36Sopenharmony_ci	ATR_DST_AXIM_2,
5762306a36Sopenharmony_ci	ATR_DST_AXIM_3,
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistruct t7xx_atr_config {
6162306a36Sopenharmony_ci	u64			src_addr;
6262306a36Sopenharmony_ci	u64			trsl_addr;
6362306a36Sopenharmony_ci	u64			size;
6462306a36Sopenharmony_ci	u32			port;
6562306a36Sopenharmony_ci	u32			table;
6662306a36Sopenharmony_ci	enum t7xx_atr_dst_port	trsl_id;
6762306a36Sopenharmony_ci	u32			transparent;
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void t7xx_pcie_mac_atr_tables_dis(void __iomem *pbase, enum t7xx_atr_src_port port)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	void __iomem *reg;
7362306a36Sopenharmony_ci	int i, offset;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	for (i = 0; i < ATR_TABLE_NUM_PER_ATR; i++) {
7662306a36Sopenharmony_ci		offset = ATR_PORT_OFFSET * port + ATR_TABLE_OFFSET * i;
7762306a36Sopenharmony_ci		reg = pbase + ATR_PCIE_WIN0_T0_ATR_PARAM_SRC_ADDR + offset;
7862306a36Sopenharmony_ci		iowrite64(0, reg);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int t7xx_pcie_mac_atr_cfg(struct t7xx_pci_dev *t7xx_dev, struct t7xx_atr_config *cfg)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct device *dev = &t7xx_dev->pdev->dev;
8562306a36Sopenharmony_ci	void __iomem *pbase = IREG_BASE(t7xx_dev);
8662306a36Sopenharmony_ci	int atr_size, pos, offset;
8762306a36Sopenharmony_ci	void __iomem *reg;
8862306a36Sopenharmony_ci	u64 value;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (cfg->transparent) {
9162306a36Sopenharmony_ci		/* No address conversion is performed */
9262306a36Sopenharmony_ci		atr_size = ATR_TRANSPARENT_SIZE;
9362306a36Sopenharmony_ci	} else {
9462306a36Sopenharmony_ci		if (cfg->src_addr & (cfg->size - 1)) {
9562306a36Sopenharmony_ci			dev_err(dev, "Source address is not aligned to size\n");
9662306a36Sopenharmony_ci			return -EINVAL;
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		if (cfg->trsl_addr & (cfg->size - 1)) {
10062306a36Sopenharmony_ci			dev_err(dev, "Translation address %llx is not aligned to size %llx\n",
10162306a36Sopenharmony_ci				cfg->trsl_addr, cfg->size - 1);
10262306a36Sopenharmony_ci			return -EINVAL;
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		pos = __ffs64(cfg->size);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		/* HW calculates the address translation space as 2^(atr_size + 1) */
10862306a36Sopenharmony_ci		atr_size = pos - 1;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	offset = ATR_PORT_OFFSET * cfg->port + ATR_TABLE_OFFSET * cfg->table;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	reg = pbase + ATR_PCIE_WIN0_T0_TRSL_ADDR + offset;
11462306a36Sopenharmony_ci	value = cfg->trsl_addr & ATR_PCIE_WIN0_ADDR_ALGMT;
11562306a36Sopenharmony_ci	iowrite64(value, reg);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	reg = pbase + ATR_PCIE_WIN0_T0_TRSL_PARAM + offset;
11862306a36Sopenharmony_ci	iowrite32(cfg->trsl_id, reg);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	reg = pbase + ATR_PCIE_WIN0_T0_ATR_PARAM_SRC_ADDR + offset;
12162306a36Sopenharmony_ci	value = (cfg->src_addr & ATR_PCIE_WIN0_ADDR_ALGMT) | (atr_size << 1) | BIT(0);
12262306a36Sopenharmony_ci	iowrite64(value, reg);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* Ensure ATR is set */
12562306a36Sopenharmony_ci	ioread64(reg);
12662306a36Sopenharmony_ci	return 0;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/**
13062306a36Sopenharmony_ci * t7xx_pcie_mac_atr_init() - Initialize address translation.
13162306a36Sopenharmony_ci * @t7xx_dev: MTK device.
13262306a36Sopenharmony_ci *
13362306a36Sopenharmony_ci * Setup ATR for ports & device.
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_civoid t7xx_pcie_mac_atr_init(struct t7xx_pci_dev *t7xx_dev)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct t7xx_atr_config cfg;
13862306a36Sopenharmony_ci	u32 i;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* Disable for all ports */
14162306a36Sopenharmony_ci	for (i = ATR_SRC_PCI_WIN0; i <= ATR_SRC_AXIS_3; i++)
14262306a36Sopenharmony_ci		t7xx_pcie_mac_atr_tables_dis(IREG_BASE(t7xx_dev), i);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	memset(&cfg, 0, sizeof(cfg));
14562306a36Sopenharmony_ci	/* Config ATR for RC to access device's register */
14662306a36Sopenharmony_ci	cfg.src_addr = pci_resource_start(t7xx_dev->pdev, T7XX_PCIE_REG_BAR);
14762306a36Sopenharmony_ci	cfg.size = T7XX_PCIE_REG_SIZE_CHIP;
14862306a36Sopenharmony_ci	cfg.trsl_addr = T7XX_PCIE_REG_TRSL_ADDR_CHIP;
14962306a36Sopenharmony_ci	cfg.port = T7XX_PCIE_REG_PORT;
15062306a36Sopenharmony_ci	cfg.table = T7XX_PCIE_REG_TABLE_NUM;
15162306a36Sopenharmony_ci	cfg.trsl_id = T7XX_PCIE_REG_TRSL_PORT;
15262306a36Sopenharmony_ci	t7xx_pcie_mac_atr_tables_dis(IREG_BASE(t7xx_dev), cfg.port);
15362306a36Sopenharmony_ci	t7xx_pcie_mac_atr_cfg(t7xx_dev, &cfg);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	t7xx_dev->base_addr.pcie_dev_reg_trsl_addr = T7XX_PCIE_REG_TRSL_ADDR_CHIP;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* Config ATR for EP to access RC's memory */
15862306a36Sopenharmony_ci	for (i = T7XX_PCIE_DEV_DMA_PORT_START; i <= T7XX_PCIE_DEV_DMA_PORT_END; i++) {
15962306a36Sopenharmony_ci		cfg.src_addr = T7XX_PCIE_DEV_DMA_SRC_ADDR;
16062306a36Sopenharmony_ci		cfg.size = T7XX_PCIE_DEV_DMA_SIZE;
16162306a36Sopenharmony_ci		cfg.trsl_addr = T7XX_PCIE_DEV_DMA_TRSL_ADDR;
16262306a36Sopenharmony_ci		cfg.port = i;
16362306a36Sopenharmony_ci		cfg.table = T7XX_PCIE_DEV_DMA_TABLE_NUM;
16462306a36Sopenharmony_ci		cfg.trsl_id = ATR_DST_PCI_TRX;
16562306a36Sopenharmony_ci		cfg.transparent = T7XX_PCIE_DEV_DMA_TRANSPARENT;
16662306a36Sopenharmony_ci		t7xx_pcie_mac_atr_tables_dis(IREG_BASE(t7xx_dev), cfg.port);
16762306a36Sopenharmony_ci		t7xx_pcie_mac_atr_cfg(t7xx_dev, &cfg);
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/**
17262306a36Sopenharmony_ci * t7xx_pcie_mac_enable_disable_int() - Enable/disable interrupts.
17362306a36Sopenharmony_ci * @t7xx_dev: MTK device.
17462306a36Sopenharmony_ci * @enable: Enable/disable.
17562306a36Sopenharmony_ci *
17662306a36Sopenharmony_ci * Enable or disable device interrupts.
17762306a36Sopenharmony_ci */
17862306a36Sopenharmony_cistatic void t7xx_pcie_mac_enable_disable_int(struct t7xx_pci_dev *t7xx_dev, bool enable)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	u32 value;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	value = ioread32(IREG_BASE(t7xx_dev) + ISTAT_HST_CTRL);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (enable)
18562306a36Sopenharmony_ci		value &= ~ISTAT_HST_CTRL_DIS;
18662306a36Sopenharmony_ci	else
18762306a36Sopenharmony_ci		value |= ISTAT_HST_CTRL_DIS;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	iowrite32(value, IREG_BASE(t7xx_dev) + ISTAT_HST_CTRL);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_civoid t7xx_pcie_mac_interrupts_en(struct t7xx_pci_dev *t7xx_dev)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	t7xx_pcie_mac_enable_disable_int(t7xx_dev, true);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_civoid t7xx_pcie_mac_interrupts_dis(struct t7xx_pci_dev *t7xx_dev)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	t7xx_pcie_mac_enable_disable_int(t7xx_dev, false);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/**
20362306a36Sopenharmony_ci * t7xx_pcie_mac_clear_set_int() - Clear/set interrupt by type.
20462306a36Sopenharmony_ci * @t7xx_dev: MTK device.
20562306a36Sopenharmony_ci * @int_type: Interrupt type.
20662306a36Sopenharmony_ci * @clear: Clear/set.
20762306a36Sopenharmony_ci *
20862306a36Sopenharmony_ci * Clear or set device interrupt by type.
20962306a36Sopenharmony_ci */
21062306a36Sopenharmony_cistatic void t7xx_pcie_mac_clear_set_int(struct t7xx_pci_dev *t7xx_dev,
21162306a36Sopenharmony_ci					enum t7xx_int int_type, bool clear)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	void __iomem *reg;
21462306a36Sopenharmony_ci	u32 val;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (clear)
21762306a36Sopenharmony_ci		reg = IREG_BASE(t7xx_dev) + IMASK_HOST_MSIX_CLR_GRP0_0;
21862306a36Sopenharmony_ci	else
21962306a36Sopenharmony_ci		reg = IREG_BASE(t7xx_dev) + IMASK_HOST_MSIX_SET_GRP0_0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	val = BIT(EXT_INT_START + int_type);
22262306a36Sopenharmony_ci	iowrite32(val, reg);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_civoid t7xx_pcie_mac_clear_int(struct t7xx_pci_dev *t7xx_dev, enum t7xx_int int_type)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	t7xx_pcie_mac_clear_set_int(t7xx_dev, int_type, true);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_civoid t7xx_pcie_mac_set_int(struct t7xx_pci_dev *t7xx_dev, enum t7xx_int int_type)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	t7xx_pcie_mac_clear_set_int(t7xx_dev, int_type, false);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci/**
23662306a36Sopenharmony_ci * t7xx_pcie_mac_clear_int_status() - Clear interrupt status by type.
23762306a36Sopenharmony_ci * @t7xx_dev: MTK device.
23862306a36Sopenharmony_ci * @int_type: Interrupt type.
23962306a36Sopenharmony_ci *
24062306a36Sopenharmony_ci * Enable or disable device interrupts' status by type.
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_civoid t7xx_pcie_mac_clear_int_status(struct t7xx_pci_dev *t7xx_dev, enum t7xx_int int_type)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	void __iomem *reg = IREG_BASE(t7xx_dev) + MSIX_ISTAT_HST_GRP0_0;
24562306a36Sopenharmony_ci	u32 val = BIT(EXT_INT_START + int_type);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	iowrite32(val, reg);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/**
25162306a36Sopenharmony_ci * t7xx_pcie_set_mac_msix_cfg() - Write MSIX control configuration.
25262306a36Sopenharmony_ci * @t7xx_dev: MTK device.
25362306a36Sopenharmony_ci * @irq_count: Number of MSIX IRQ vectors.
25462306a36Sopenharmony_ci *
25562306a36Sopenharmony_ci * Write IRQ count to device.
25662306a36Sopenharmony_ci */
25762306a36Sopenharmony_civoid t7xx_pcie_set_mac_msix_cfg(struct t7xx_pci_dev *t7xx_dev, unsigned int irq_count)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	u32 val = ffs(irq_count) * 2 - 1;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	iowrite32(val, IREG_BASE(t7xx_dev) + T7XX_PCIE_CFG_MSIX);
26262306a36Sopenharmony_ci}
263