1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2021, MediaTek Inc. |
4 | * Copyright (c) 2021-2022, Intel Corporation. |
5 | * |
6 | * Authors: |
7 | * Haijun Liu <haijun.liu@mediatek.com> |
8 | * Moises Veleta <moises.veleta@intel.com> |
9 | * Sreehari Kancharla <sreehari.kancharla@intel.com> |
10 | * |
11 | * Contributors: |
12 | * Amir Hanania <amir.hanania@intel.com> |
13 | * Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com> |
14 | * Ricardo Martinez <ricardo.martinez@linux.intel.com> |
15 | */ |
16 | |
17 | #include <linux/bits.h> |
18 | #include <linux/bitops.h> |
19 | #include <linux/device.h> |
20 | #include <linux/io-64-nonatomic-lo-hi.h> |
21 | #include <linux/pci.h> |
22 | #include <linux/string.h> |
23 | #include <linux/types.h> |
24 | |
25 | #include "t7xx_pci.h" |
26 | #include "t7xx_pcie_mac.h" |
27 | #include "t7xx_reg.h" |
28 | |
29 | #define T7XX_PCIE_REG_BAR 2 |
30 | #define T7XX_PCIE_REG_PORT ATR_SRC_PCI_WIN0 |
31 | #define T7XX_PCIE_REG_TABLE_NUM 0 |
32 | #define T7XX_PCIE_REG_TRSL_PORT ATR_DST_AXIM_0 |
33 | |
34 | #define T7XX_PCIE_DEV_DMA_PORT_START ATR_SRC_AXIS_0 |
35 | #define T7XX_PCIE_DEV_DMA_PORT_END ATR_SRC_AXIS_2 |
36 | #define T7XX_PCIE_DEV_DMA_TABLE_NUM 0 |
37 | #define T7XX_PCIE_DEV_DMA_TRSL_ADDR 0 |
38 | #define T7XX_PCIE_DEV_DMA_SRC_ADDR 0 |
39 | #define T7XX_PCIE_DEV_DMA_TRANSPARENT 1 |
40 | #define T7XX_PCIE_DEV_DMA_SIZE 0 |
41 | |
42 | enum t7xx_atr_src_port { |
43 | ATR_SRC_PCI_WIN0, |
44 | ATR_SRC_PCI_WIN1, |
45 | ATR_SRC_AXIS_0, |
46 | ATR_SRC_AXIS_1, |
47 | ATR_SRC_AXIS_2, |
48 | ATR_SRC_AXIS_3, |
49 | }; |
50 | |
51 | enum t7xx_atr_dst_port { |
52 | ATR_DST_PCI_TRX, |
53 | ATR_DST_PCI_CONFIG, |
54 | ATR_DST_AXIM_0 = 4, |
55 | ATR_DST_AXIM_1, |
56 | ATR_DST_AXIM_2, |
57 | ATR_DST_AXIM_3, |
58 | }; |
59 | |
60 | struct t7xx_atr_config { |
61 | u64 src_addr; |
62 | u64 trsl_addr; |
63 | u64 size; |
64 | u32 port; |
65 | u32 table; |
66 | enum t7xx_atr_dst_port trsl_id; |
67 | u32 transparent; |
68 | }; |
69 | |
70 | static void t7xx_pcie_mac_atr_tables_dis(void __iomem *pbase, enum t7xx_atr_src_port port) |
71 | { |
72 | void __iomem *reg; |
73 | int i, offset; |
74 | |
75 | for (i = 0; i < ATR_TABLE_NUM_PER_ATR; i++) { |
76 | offset = ATR_PORT_OFFSET * port + ATR_TABLE_OFFSET * i; |
77 | reg = pbase + ATR_PCIE_WIN0_T0_ATR_PARAM_SRC_ADDR + offset; |
78 | iowrite64_lo_hi(val: 0, addr: reg); |
79 | } |
80 | } |
81 | |
82 | static int t7xx_pcie_mac_atr_cfg(struct t7xx_pci_dev *t7xx_dev, struct t7xx_atr_config *cfg) |
83 | { |
84 | struct device *dev = &t7xx_dev->pdev->dev; |
85 | void __iomem *pbase = IREG_BASE(t7xx_dev); |
86 | int atr_size, pos, offset; |
87 | void __iomem *reg; |
88 | u64 value; |
89 | |
90 | if (cfg->transparent) { |
91 | /* No address conversion is performed */ |
92 | atr_size = ATR_TRANSPARENT_SIZE; |
93 | } else { |
94 | if (cfg->src_addr & (cfg->size - 1)) { |
95 | dev_err(dev, "Source address is not aligned to size\n" ); |
96 | return -EINVAL; |
97 | } |
98 | |
99 | if (cfg->trsl_addr & (cfg->size - 1)) { |
100 | dev_err(dev, "Translation address %llx is not aligned to size %llx\n" , |
101 | cfg->trsl_addr, cfg->size - 1); |
102 | return -EINVAL; |
103 | } |
104 | |
105 | pos = __ffs64(word: cfg->size); |
106 | |
107 | /* HW calculates the address translation space as 2^(atr_size + 1) */ |
108 | atr_size = pos - 1; |
109 | } |
110 | |
111 | offset = ATR_PORT_OFFSET * cfg->port + ATR_TABLE_OFFSET * cfg->table; |
112 | |
113 | reg = pbase + ATR_PCIE_WIN0_T0_TRSL_ADDR + offset; |
114 | value = cfg->trsl_addr & ATR_PCIE_WIN0_ADDR_ALGMT; |
115 | iowrite64_lo_hi(val: value, addr: reg); |
116 | |
117 | reg = pbase + ATR_PCIE_WIN0_T0_TRSL_PARAM + offset; |
118 | iowrite32(cfg->trsl_id, reg); |
119 | |
120 | reg = pbase + ATR_PCIE_WIN0_T0_ATR_PARAM_SRC_ADDR + offset; |
121 | value = (cfg->src_addr & ATR_PCIE_WIN0_ADDR_ALGMT) | (atr_size << 1) | BIT(0); |
122 | iowrite64_lo_hi(val: value, addr: reg); |
123 | |
124 | /* Ensure ATR is set */ |
125 | ioread64_lo_hi(addr: reg); |
126 | return 0; |
127 | } |
128 | |
129 | /** |
130 | * t7xx_pcie_mac_atr_init() - Initialize address translation. |
131 | * @t7xx_dev: MTK device. |
132 | * |
133 | * Setup ATR for ports & device. |
134 | */ |
135 | void t7xx_pcie_mac_atr_init(struct t7xx_pci_dev *t7xx_dev) |
136 | { |
137 | struct t7xx_atr_config cfg; |
138 | u32 i; |
139 | |
140 | /* Disable for all ports */ |
141 | for (i = ATR_SRC_PCI_WIN0; i <= ATR_SRC_AXIS_3; i++) |
142 | t7xx_pcie_mac_atr_tables_dis(IREG_BASE(t7xx_dev), port: i); |
143 | |
144 | memset(&cfg, 0, sizeof(cfg)); |
145 | /* Config ATR for RC to access device's register */ |
146 | cfg.src_addr = pci_resource_start(t7xx_dev->pdev, T7XX_PCIE_REG_BAR); |
147 | cfg.size = T7XX_PCIE_REG_SIZE_CHIP; |
148 | cfg.trsl_addr = T7XX_PCIE_REG_TRSL_ADDR_CHIP; |
149 | cfg.port = T7XX_PCIE_REG_PORT; |
150 | cfg.table = T7XX_PCIE_REG_TABLE_NUM; |
151 | cfg.trsl_id = T7XX_PCIE_REG_TRSL_PORT; |
152 | t7xx_pcie_mac_atr_tables_dis(IREG_BASE(t7xx_dev), port: cfg.port); |
153 | t7xx_pcie_mac_atr_cfg(t7xx_dev, cfg: &cfg); |
154 | |
155 | t7xx_dev->base_addr.pcie_dev_reg_trsl_addr = T7XX_PCIE_REG_TRSL_ADDR_CHIP; |
156 | |
157 | /* Config ATR for EP to access RC's memory */ |
158 | for (i = T7XX_PCIE_DEV_DMA_PORT_START; i <= T7XX_PCIE_DEV_DMA_PORT_END; i++) { |
159 | cfg.src_addr = T7XX_PCIE_DEV_DMA_SRC_ADDR; |
160 | cfg.size = T7XX_PCIE_DEV_DMA_SIZE; |
161 | cfg.trsl_addr = T7XX_PCIE_DEV_DMA_TRSL_ADDR; |
162 | cfg.port = i; |
163 | cfg.table = T7XX_PCIE_DEV_DMA_TABLE_NUM; |
164 | cfg.trsl_id = ATR_DST_PCI_TRX; |
165 | cfg.transparent = T7XX_PCIE_DEV_DMA_TRANSPARENT; |
166 | t7xx_pcie_mac_atr_tables_dis(IREG_BASE(t7xx_dev), port: cfg.port); |
167 | t7xx_pcie_mac_atr_cfg(t7xx_dev, cfg: &cfg); |
168 | } |
169 | } |
170 | |
171 | /** |
172 | * t7xx_pcie_mac_enable_disable_int() - Enable/disable interrupts. |
173 | * @t7xx_dev: MTK device. |
174 | * @enable: Enable/disable. |
175 | * |
176 | * Enable or disable device interrupts. |
177 | */ |
178 | static void t7xx_pcie_mac_enable_disable_int(struct t7xx_pci_dev *t7xx_dev, bool enable) |
179 | { |
180 | u32 value; |
181 | |
182 | value = ioread32(IREG_BASE(t7xx_dev) + ISTAT_HST_CTRL); |
183 | |
184 | if (enable) |
185 | value &= ~ISTAT_HST_CTRL_DIS; |
186 | else |
187 | value |= ISTAT_HST_CTRL_DIS; |
188 | |
189 | iowrite32(value, IREG_BASE(t7xx_dev) + ISTAT_HST_CTRL); |
190 | } |
191 | |
192 | void t7xx_pcie_mac_interrupts_en(struct t7xx_pci_dev *t7xx_dev) |
193 | { |
194 | t7xx_pcie_mac_enable_disable_int(t7xx_dev, enable: true); |
195 | } |
196 | |
197 | void t7xx_pcie_mac_interrupts_dis(struct t7xx_pci_dev *t7xx_dev) |
198 | { |
199 | t7xx_pcie_mac_enable_disable_int(t7xx_dev, enable: false); |
200 | } |
201 | |
202 | /** |
203 | * t7xx_pcie_mac_clear_set_int() - Clear/set interrupt by type. |
204 | * @t7xx_dev: MTK device. |
205 | * @int_type: Interrupt type. |
206 | * @clear: Clear/set. |
207 | * |
208 | * Clear or set device interrupt by type. |
209 | */ |
210 | static void t7xx_pcie_mac_clear_set_int(struct t7xx_pci_dev *t7xx_dev, |
211 | enum t7xx_int int_type, bool clear) |
212 | { |
213 | void __iomem *reg; |
214 | u32 val; |
215 | |
216 | if (clear) |
217 | reg = IREG_BASE(t7xx_dev) + IMASK_HOST_MSIX_CLR_GRP0_0; |
218 | else |
219 | reg = IREG_BASE(t7xx_dev) + IMASK_HOST_MSIX_SET_GRP0_0; |
220 | |
221 | val = BIT(EXT_INT_START + int_type); |
222 | iowrite32(val, reg); |
223 | } |
224 | |
225 | void t7xx_pcie_mac_clear_int(struct t7xx_pci_dev *t7xx_dev, enum t7xx_int int_type) |
226 | { |
227 | t7xx_pcie_mac_clear_set_int(t7xx_dev, int_type, clear: true); |
228 | } |
229 | |
230 | void t7xx_pcie_mac_set_int(struct t7xx_pci_dev *t7xx_dev, enum t7xx_int int_type) |
231 | { |
232 | t7xx_pcie_mac_clear_set_int(t7xx_dev, int_type, clear: false); |
233 | } |
234 | |
235 | /** |
236 | * t7xx_pcie_mac_clear_int_status() - Clear interrupt status by type. |
237 | * @t7xx_dev: MTK device. |
238 | * @int_type: Interrupt type. |
239 | * |
240 | * Enable or disable device interrupts' status by type. |
241 | */ |
242 | void t7xx_pcie_mac_clear_int_status(struct t7xx_pci_dev *t7xx_dev, enum t7xx_int int_type) |
243 | { |
244 | void __iomem *reg = IREG_BASE(t7xx_dev) + MSIX_ISTAT_HST_GRP0_0; |
245 | u32 val = BIT(EXT_INT_START + int_type); |
246 | |
247 | iowrite32(val, reg); |
248 | } |
249 | |
250 | /** |
251 | * t7xx_pcie_set_mac_msix_cfg() - Write MSIX control configuration. |
252 | * @t7xx_dev: MTK device. |
253 | * @irq_count: Number of MSIX IRQ vectors. |
254 | * |
255 | * Write IRQ count to device. |
256 | */ |
257 | void t7xx_pcie_set_mac_msix_cfg(struct t7xx_pci_dev *t7xx_dev, unsigned int irq_count) |
258 | { |
259 | u32 val = ffs(irq_count) * 2 - 1; |
260 | |
261 | iowrite32(val, IREG_BASE(t7xx_dev) + T7XX_PCIE_CFG_MSIX); |
262 | } |
263 | |