113e920d9SHaijun Liu // SPDX-License-Identifier: GPL-2.0-only
213e920d9SHaijun Liu /*
313e920d9SHaijun Liu * Copyright (c) 2021, MediaTek Inc.
413e920d9SHaijun Liu * Copyright (c) 2021-2022, Intel Corporation.
513e920d9SHaijun Liu *
613e920d9SHaijun Liu * Authors:
713e920d9SHaijun Liu * Haijun Liu <haijun.liu@mediatek.com>
813e920d9SHaijun Liu * Moises Veleta <moises.veleta@intel.com>
913e920d9SHaijun Liu * Sreehari Kancharla <sreehari.kancharla@intel.com>
1013e920d9SHaijun Liu *
1113e920d9SHaijun Liu * Contributors:
1213e920d9SHaijun Liu * Amir Hanania <amir.hanania@intel.com>
1313e920d9SHaijun Liu * Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com>
1413e920d9SHaijun Liu * Ricardo Martinez <ricardo.martinez@linux.intel.com>
1513e920d9SHaijun Liu */
1613e920d9SHaijun Liu
1713e920d9SHaijun Liu #include <linux/bits.h>
1813e920d9SHaijun Liu #include <linux/bitops.h>
1913e920d9SHaijun Liu #include <linux/device.h>
2013e920d9SHaijun Liu #include <linux/io-64-nonatomic-lo-hi.h>
2113e920d9SHaijun Liu #include <linux/pci.h>
2213e920d9SHaijun Liu #include <linux/string.h>
2313e920d9SHaijun Liu #include <linux/types.h>
2413e920d9SHaijun Liu
2513e920d9SHaijun Liu #include "t7xx_pci.h"
2613e920d9SHaijun Liu #include "t7xx_pcie_mac.h"
2713e920d9SHaijun Liu #include "t7xx_reg.h"
2813e920d9SHaijun Liu
2913e920d9SHaijun Liu #define T7XX_PCIE_REG_BAR 2
3013e920d9SHaijun Liu #define T7XX_PCIE_REG_PORT ATR_SRC_PCI_WIN0
3113e920d9SHaijun Liu #define T7XX_PCIE_REG_TABLE_NUM 0
3213e920d9SHaijun Liu #define T7XX_PCIE_REG_TRSL_PORT ATR_DST_AXIM_0
3313e920d9SHaijun Liu
3413e920d9SHaijun Liu #define T7XX_PCIE_DEV_DMA_PORT_START ATR_SRC_AXIS_0
3513e920d9SHaijun Liu #define T7XX_PCIE_DEV_DMA_PORT_END ATR_SRC_AXIS_2
3613e920d9SHaijun Liu #define T7XX_PCIE_DEV_DMA_TABLE_NUM 0
3713e920d9SHaijun Liu #define T7XX_PCIE_DEV_DMA_TRSL_ADDR 0
3813e920d9SHaijun Liu #define T7XX_PCIE_DEV_DMA_SRC_ADDR 0
3913e920d9SHaijun Liu #define T7XX_PCIE_DEV_DMA_TRANSPARENT 1
4013e920d9SHaijun Liu #define T7XX_PCIE_DEV_DMA_SIZE 0
4113e920d9SHaijun Liu
4213e920d9SHaijun Liu enum t7xx_atr_src_port {
4313e920d9SHaijun Liu ATR_SRC_PCI_WIN0,
4413e920d9SHaijun Liu ATR_SRC_PCI_WIN1,
4513e920d9SHaijun Liu ATR_SRC_AXIS_0,
4613e920d9SHaijun Liu ATR_SRC_AXIS_1,
4713e920d9SHaijun Liu ATR_SRC_AXIS_2,
4813e920d9SHaijun Liu ATR_SRC_AXIS_3,
4913e920d9SHaijun Liu };
5013e920d9SHaijun Liu
5113e920d9SHaijun Liu enum t7xx_atr_dst_port {
5213e920d9SHaijun Liu ATR_DST_PCI_TRX,
5313e920d9SHaijun Liu ATR_DST_PCI_CONFIG,
5413e920d9SHaijun Liu ATR_DST_AXIM_0 = 4,
5513e920d9SHaijun Liu ATR_DST_AXIM_1,
5613e920d9SHaijun Liu ATR_DST_AXIM_2,
5713e920d9SHaijun Liu ATR_DST_AXIM_3,
5813e920d9SHaijun Liu };
5913e920d9SHaijun Liu
6013e920d9SHaijun Liu struct t7xx_atr_config {
6113e920d9SHaijun Liu u64 src_addr;
6213e920d9SHaijun Liu u64 trsl_addr;
6313e920d9SHaijun Liu u64 size;
6413e920d9SHaijun Liu u32 port;
6513e920d9SHaijun Liu u32 table;
6613e920d9SHaijun Liu enum t7xx_atr_dst_port trsl_id;
6713e920d9SHaijun Liu u32 transparent;
6813e920d9SHaijun Liu };
6913e920d9SHaijun Liu
t7xx_pcie_mac_atr_tables_dis(void __iomem * pbase,enum t7xx_atr_src_port port)7013e920d9SHaijun Liu static void t7xx_pcie_mac_atr_tables_dis(void __iomem *pbase, enum t7xx_atr_src_port port)
7113e920d9SHaijun Liu {
7213e920d9SHaijun Liu void __iomem *reg;
7313e920d9SHaijun Liu int i, offset;
7413e920d9SHaijun Liu
7513e920d9SHaijun Liu for (i = 0; i < ATR_TABLE_NUM_PER_ATR; i++) {
7613e920d9SHaijun Liu offset = ATR_PORT_OFFSET * port + ATR_TABLE_OFFSET * i;
7713e920d9SHaijun Liu reg = pbase + ATR_PCIE_WIN0_T0_ATR_PARAM_SRC_ADDR + offset;
78*2e22c9cbSBjørn Mork iowrite64_lo_hi(0, reg);
7913e920d9SHaijun Liu }
8013e920d9SHaijun Liu }
8113e920d9SHaijun Liu
t7xx_pcie_mac_atr_cfg(struct t7xx_pci_dev * t7xx_dev,struct t7xx_atr_config * cfg)8213e920d9SHaijun Liu static int t7xx_pcie_mac_atr_cfg(struct t7xx_pci_dev *t7xx_dev, struct t7xx_atr_config *cfg)
8313e920d9SHaijun Liu {
8413e920d9SHaijun Liu struct device *dev = &t7xx_dev->pdev->dev;
8513e920d9SHaijun Liu void __iomem *pbase = IREG_BASE(t7xx_dev);
8613e920d9SHaijun Liu int atr_size, pos, offset;
8713e920d9SHaijun Liu void __iomem *reg;
8813e920d9SHaijun Liu u64 value;
8913e920d9SHaijun Liu
9013e920d9SHaijun Liu if (cfg->transparent) {
9113e920d9SHaijun Liu /* No address conversion is performed */
9213e920d9SHaijun Liu atr_size = ATR_TRANSPARENT_SIZE;
9313e920d9SHaijun Liu } else {
9413e920d9SHaijun Liu if (cfg->src_addr & (cfg->size - 1)) {
9513e920d9SHaijun Liu dev_err(dev, "Source address is not aligned to size\n");
9613e920d9SHaijun Liu return -EINVAL;
9713e920d9SHaijun Liu }
9813e920d9SHaijun Liu
9913e920d9SHaijun Liu if (cfg->trsl_addr & (cfg->size - 1)) {
10013e920d9SHaijun Liu dev_err(dev, "Translation address %llx is not aligned to size %llx\n",
10113e920d9SHaijun Liu cfg->trsl_addr, cfg->size - 1);
10213e920d9SHaijun Liu return -EINVAL;
10313e920d9SHaijun Liu }
10413e920d9SHaijun Liu
10513e920d9SHaijun Liu pos = __ffs64(cfg->size);
10613e920d9SHaijun Liu
10713e920d9SHaijun Liu /* HW calculates the address translation space as 2^(atr_size + 1) */
10813e920d9SHaijun Liu atr_size = pos - 1;
10913e920d9SHaijun Liu }
11013e920d9SHaijun Liu
11113e920d9SHaijun Liu offset = ATR_PORT_OFFSET * cfg->port + ATR_TABLE_OFFSET * cfg->table;
11213e920d9SHaijun Liu
11313e920d9SHaijun Liu reg = pbase + ATR_PCIE_WIN0_T0_TRSL_ADDR + offset;
11413e920d9SHaijun Liu value = cfg->trsl_addr & ATR_PCIE_WIN0_ADDR_ALGMT;
115*2e22c9cbSBjørn Mork iowrite64_lo_hi(value, reg);
11613e920d9SHaijun Liu
11713e920d9SHaijun Liu reg = pbase + ATR_PCIE_WIN0_T0_TRSL_PARAM + offset;
11813e920d9SHaijun Liu iowrite32(cfg->trsl_id, reg);
11913e920d9SHaijun Liu
12013e920d9SHaijun Liu reg = pbase + ATR_PCIE_WIN0_T0_ATR_PARAM_SRC_ADDR + offset;
12113e920d9SHaijun Liu value = (cfg->src_addr & ATR_PCIE_WIN0_ADDR_ALGMT) | (atr_size << 1) | BIT(0);
122*2e22c9cbSBjørn Mork iowrite64_lo_hi(value, reg);
12313e920d9SHaijun Liu
12413e920d9SHaijun Liu /* Ensure ATR is set */
125*2e22c9cbSBjørn Mork ioread64_lo_hi(reg);
12613e920d9SHaijun Liu return 0;
12713e920d9SHaijun Liu }
12813e920d9SHaijun Liu
12913e920d9SHaijun Liu /**
13013e920d9SHaijun Liu * t7xx_pcie_mac_atr_init() - Initialize address translation.
13113e920d9SHaijun Liu * @t7xx_dev: MTK device.
13213e920d9SHaijun Liu *
13313e920d9SHaijun Liu * Setup ATR for ports & device.
13413e920d9SHaijun Liu */
t7xx_pcie_mac_atr_init(struct t7xx_pci_dev * t7xx_dev)13513e920d9SHaijun Liu void t7xx_pcie_mac_atr_init(struct t7xx_pci_dev *t7xx_dev)
13613e920d9SHaijun Liu {
13713e920d9SHaijun Liu struct t7xx_atr_config cfg;
13813e920d9SHaijun Liu u32 i;
13913e920d9SHaijun Liu
14013e920d9SHaijun Liu /* Disable for all ports */
14113e920d9SHaijun Liu for (i = ATR_SRC_PCI_WIN0; i <= ATR_SRC_AXIS_3; i++)
14213e920d9SHaijun Liu t7xx_pcie_mac_atr_tables_dis(IREG_BASE(t7xx_dev), i);
14313e920d9SHaijun Liu
14413e920d9SHaijun Liu memset(&cfg, 0, sizeof(cfg));
14513e920d9SHaijun Liu /* Config ATR for RC to access device's register */
14613e920d9SHaijun Liu cfg.src_addr = pci_resource_start(t7xx_dev->pdev, T7XX_PCIE_REG_BAR);
14713e920d9SHaijun Liu cfg.size = T7XX_PCIE_REG_SIZE_CHIP;
14813e920d9SHaijun Liu cfg.trsl_addr = T7XX_PCIE_REG_TRSL_ADDR_CHIP;
14913e920d9SHaijun Liu cfg.port = T7XX_PCIE_REG_PORT;
15013e920d9SHaijun Liu cfg.table = T7XX_PCIE_REG_TABLE_NUM;
15113e920d9SHaijun Liu cfg.trsl_id = T7XX_PCIE_REG_TRSL_PORT;
15213e920d9SHaijun Liu t7xx_pcie_mac_atr_tables_dis(IREG_BASE(t7xx_dev), cfg.port);
15313e920d9SHaijun Liu t7xx_pcie_mac_atr_cfg(t7xx_dev, &cfg);
15413e920d9SHaijun Liu
15513e920d9SHaijun Liu t7xx_dev->base_addr.pcie_dev_reg_trsl_addr = T7XX_PCIE_REG_TRSL_ADDR_CHIP;
15613e920d9SHaijun Liu
15713e920d9SHaijun Liu /* Config ATR for EP to access RC's memory */
15813e920d9SHaijun Liu for (i = T7XX_PCIE_DEV_DMA_PORT_START; i <= T7XX_PCIE_DEV_DMA_PORT_END; i++) {
15913e920d9SHaijun Liu cfg.src_addr = T7XX_PCIE_DEV_DMA_SRC_ADDR;
16013e920d9SHaijun Liu cfg.size = T7XX_PCIE_DEV_DMA_SIZE;
16113e920d9SHaijun Liu cfg.trsl_addr = T7XX_PCIE_DEV_DMA_TRSL_ADDR;
16213e920d9SHaijun Liu cfg.port = i;
16313e920d9SHaijun Liu cfg.table = T7XX_PCIE_DEV_DMA_TABLE_NUM;
16413e920d9SHaijun Liu cfg.trsl_id = ATR_DST_PCI_TRX;
16513e920d9SHaijun Liu cfg.transparent = T7XX_PCIE_DEV_DMA_TRANSPARENT;
16613e920d9SHaijun Liu t7xx_pcie_mac_atr_tables_dis(IREG_BASE(t7xx_dev), cfg.port);
16713e920d9SHaijun Liu t7xx_pcie_mac_atr_cfg(t7xx_dev, &cfg);
16813e920d9SHaijun Liu }
16913e920d9SHaijun Liu }
17013e920d9SHaijun Liu
17113e920d9SHaijun Liu /**
17213e920d9SHaijun Liu * t7xx_pcie_mac_enable_disable_int() - Enable/disable interrupts.
17313e920d9SHaijun Liu * @t7xx_dev: MTK device.
17413e920d9SHaijun Liu * @enable: Enable/disable.
17513e920d9SHaijun Liu *
17613e920d9SHaijun Liu * Enable or disable device interrupts.
17713e920d9SHaijun Liu */
t7xx_pcie_mac_enable_disable_int(struct t7xx_pci_dev * t7xx_dev,bool enable)17813e920d9SHaijun Liu static void t7xx_pcie_mac_enable_disable_int(struct t7xx_pci_dev *t7xx_dev, bool enable)
17913e920d9SHaijun Liu {
18013e920d9SHaijun Liu u32 value;
18113e920d9SHaijun Liu
18213e920d9SHaijun Liu value = ioread32(IREG_BASE(t7xx_dev) + ISTAT_HST_CTRL);
18313e920d9SHaijun Liu
18413e920d9SHaijun Liu if (enable)
18513e920d9SHaijun Liu value &= ~ISTAT_HST_CTRL_DIS;
18613e920d9SHaijun Liu else
18713e920d9SHaijun Liu value |= ISTAT_HST_CTRL_DIS;
18813e920d9SHaijun Liu
18913e920d9SHaijun Liu iowrite32(value, IREG_BASE(t7xx_dev) + ISTAT_HST_CTRL);
19013e920d9SHaijun Liu }
19113e920d9SHaijun Liu
t7xx_pcie_mac_interrupts_en(struct t7xx_pci_dev * t7xx_dev)19213e920d9SHaijun Liu void t7xx_pcie_mac_interrupts_en(struct t7xx_pci_dev *t7xx_dev)
19313e920d9SHaijun Liu {
19413e920d9SHaijun Liu t7xx_pcie_mac_enable_disable_int(t7xx_dev, true);
19513e920d9SHaijun Liu }
19613e920d9SHaijun Liu
t7xx_pcie_mac_interrupts_dis(struct t7xx_pci_dev * t7xx_dev)19713e920d9SHaijun Liu void t7xx_pcie_mac_interrupts_dis(struct t7xx_pci_dev *t7xx_dev)
19813e920d9SHaijun Liu {
19913e920d9SHaijun Liu t7xx_pcie_mac_enable_disable_int(t7xx_dev, false);
20013e920d9SHaijun Liu }
20113e920d9SHaijun Liu
20213e920d9SHaijun Liu /**
20313e920d9SHaijun Liu * t7xx_pcie_mac_clear_set_int() - Clear/set interrupt by type.
20413e920d9SHaijun Liu * @t7xx_dev: MTK device.
20513e920d9SHaijun Liu * @int_type: Interrupt type.
20613e920d9SHaijun Liu * @clear: Clear/set.
20713e920d9SHaijun Liu *
20813e920d9SHaijun Liu * Clear or set device interrupt by type.
20913e920d9SHaijun Liu */
t7xx_pcie_mac_clear_set_int(struct t7xx_pci_dev * t7xx_dev,enum t7xx_int int_type,bool clear)21013e920d9SHaijun Liu static void t7xx_pcie_mac_clear_set_int(struct t7xx_pci_dev *t7xx_dev,
21113e920d9SHaijun Liu enum t7xx_int int_type, bool clear)
21213e920d9SHaijun Liu {
21313e920d9SHaijun Liu void __iomem *reg;
21413e920d9SHaijun Liu u32 val;
21513e920d9SHaijun Liu
21613e920d9SHaijun Liu if (clear)
21713e920d9SHaijun Liu reg = IREG_BASE(t7xx_dev) + IMASK_HOST_MSIX_CLR_GRP0_0;
21813e920d9SHaijun Liu else
21913e920d9SHaijun Liu reg = IREG_BASE(t7xx_dev) + IMASK_HOST_MSIX_SET_GRP0_0;
22013e920d9SHaijun Liu
22113e920d9SHaijun Liu val = BIT(EXT_INT_START + int_type);
22213e920d9SHaijun Liu iowrite32(val, reg);
22313e920d9SHaijun Liu }
22413e920d9SHaijun Liu
t7xx_pcie_mac_clear_int(struct t7xx_pci_dev * t7xx_dev,enum t7xx_int int_type)22513e920d9SHaijun Liu void t7xx_pcie_mac_clear_int(struct t7xx_pci_dev *t7xx_dev, enum t7xx_int int_type)
22613e920d9SHaijun Liu {
22713e920d9SHaijun Liu t7xx_pcie_mac_clear_set_int(t7xx_dev, int_type, true);
22813e920d9SHaijun Liu }
22913e920d9SHaijun Liu
t7xx_pcie_mac_set_int(struct t7xx_pci_dev * t7xx_dev,enum t7xx_int int_type)23013e920d9SHaijun Liu void t7xx_pcie_mac_set_int(struct t7xx_pci_dev *t7xx_dev, enum t7xx_int int_type)
23113e920d9SHaijun Liu {
23213e920d9SHaijun Liu t7xx_pcie_mac_clear_set_int(t7xx_dev, int_type, false);
23313e920d9SHaijun Liu }
23413e920d9SHaijun Liu
23513e920d9SHaijun Liu /**
23613e920d9SHaijun Liu * t7xx_pcie_mac_clear_int_status() - Clear interrupt status by type.
23713e920d9SHaijun Liu * @t7xx_dev: MTK device.
23813e920d9SHaijun Liu * @int_type: Interrupt type.
23913e920d9SHaijun Liu *
24013e920d9SHaijun Liu * Enable or disable device interrupts' status by type.
24113e920d9SHaijun Liu */
t7xx_pcie_mac_clear_int_status(struct t7xx_pci_dev * t7xx_dev,enum t7xx_int int_type)24213e920d9SHaijun Liu void t7xx_pcie_mac_clear_int_status(struct t7xx_pci_dev *t7xx_dev, enum t7xx_int int_type)
24313e920d9SHaijun Liu {
24413e920d9SHaijun Liu void __iomem *reg = IREG_BASE(t7xx_dev) + MSIX_ISTAT_HST_GRP0_0;
24513e920d9SHaijun Liu u32 val = BIT(EXT_INT_START + int_type);
24613e920d9SHaijun Liu
24713e920d9SHaijun Liu iowrite32(val, reg);
24813e920d9SHaijun Liu }
24913e920d9SHaijun Liu
25013e920d9SHaijun Liu /**
25113e920d9SHaijun Liu * t7xx_pcie_set_mac_msix_cfg() - Write MSIX control configuration.
25213e920d9SHaijun Liu * @t7xx_dev: MTK device.
25313e920d9SHaijun Liu * @irq_count: Number of MSIX IRQ vectors.
25413e920d9SHaijun Liu *
25513e920d9SHaijun Liu * Write IRQ count to device.
25613e920d9SHaijun Liu */
t7xx_pcie_set_mac_msix_cfg(struct t7xx_pci_dev * t7xx_dev,unsigned int irq_count)25713e920d9SHaijun Liu void t7xx_pcie_set_mac_msix_cfg(struct t7xx_pci_dev *t7xx_dev, unsigned int irq_count)
25813e920d9SHaijun Liu {
25913e920d9SHaijun Liu u32 val = ffs(irq_count) * 2 - 1;
26013e920d9SHaijun Liu
26113e920d9SHaijun Liu iowrite32(val, IREG_BASE(t7xx_dev) + T7XX_PCIE_CFG_MSIX);
26213e920d9SHaijun Liu }
263