Message ID | 20221116135504.258687-9-daire.mcnamara@microchip.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | PCI: microchip: Partition address translations | expand |
Context | Check | Description |
---|---|---|
conchuod/tree_selection | fail | Guessing tree name failed |
On Wed, Nov 16, 2022 at 01:55:03PM +0000, daire.mcnamara@microchip.com wrote: > From: Daire McNamara <daire.mcnamara@microchip.com> > > On Microchip PolarFire SoC the PCIe rootport is behind a set of fabric > inter connect (fic) busses that encapsulate busses like ABP/AHP, AXI-S > and AXI-M. Depending on which fic(s) the rootport is wired through to > cpu space, the rootport driver needs to take account of the address > translation done by a parent (e.g. fabric) node before setting up its > own inbound address translation tables from attached devices. Hi Daire, minor nits: s/inter connect/interconnect/ s/fic/FIC/ ? Sounds like an initialism similar to ABP, AHP, etc? s/busses/buses/ Both ok, but "buses" much more common in drivers/pci/ s/cpu/CPU/ s/rootport/Root Port/ I try to follow PCIe spec usage. Below you use "root port" (with a space). At least add the space to make consistent here. Some apply to previous commit logs, too, IIRC. > + /* > + * check for one level up; will need to adjust > + * address translation tables for these Wrap to fill 78 columns or so. Most existing comments in the file are also capitalized per normal English conventions.
On Wed, Nov 16, 2022 at 10:49:33AM -0600, Bjorn Helgaas wrote: > On Wed, Nov 16, 2022 at 01:55:03PM +0000, daire.mcnamara@microchip.com wrote: > > From: Daire McNamara <daire.mcnamara@microchip.com> > > > > On Microchip PolarFire SoC the PCIe rootport is behind a set of fabric > > inter connect (fic) busses that encapsulate busses like ABP/AHP, AXI-S > > and AXI-M. Depending on which fic(s) the rootport is wired through to > > cpu space, the rootport driver needs to take account of the address > > translation done by a parent (e.g. fabric) node before setting up its > > own inbound address translation tables from attached devices. > > Hi Daire, minor nits: > > s/inter connect/interconnect/ > s/fic/FIC/ ? Sounds like an initialism similar to ABP, AHP, etc? Daire, we've been living a lie. The TRM says "Fabric Interface Controllers (FICs)" so I think we should switch the that wording. Fits the acronym better too..
Hi, I love your patch! Perhaps something to improve: [auto build test WARNING on 3c1f24109dfc4fb1a3730ed237e50183c6bb26b3] url: https://github.com/intel-lab-lkp/linux/commits/daire-mcnamara-microchip-com/PCI-microchip-Partition-address-translations/20221116-220208 base: 3c1f24109dfc4fb1a3730ed237e50183c6bb26b3 patch link: https://lore.kernel.org/r/20221116135504.258687-9-daire.mcnamara%40microchip.com patch subject: [PATCH v1 8/9] PCI: microchip: Partition inbound address translation config: ia64-allyesconfig compiler: ia64-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/41006bdc423e557398463818442a7607a8111347 git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review daire-mcnamara-microchip-com/PCI-microchip-Partition-address-translations/20221116-220208 git checkout 41006bdc423e557398463818442a7607a8111347 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=ia64 SHELL=/bin/bash drivers/pci/controller/ If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): drivers/pci/controller/pcie-microchip-host.c: In function 'mc_check_for_parent_dma_range_handling': >> drivers/pci/controller/pcie-microchip-host.c:1212:13: warning: variable 'end_pci' set but not used [-Wunused-but-set-variable] 1212 | u64 end_pci = 0; | ^~~~~~~ drivers/pci/controller/pcie-microchip-host.c: In function 'mc_host_probe': drivers/pci/controller/pcie-microchip-host.c:1364:23: warning: variable 'ctrl_base_addr' set but not used [-Wunused-but-set-variable] 1364 | void __iomem *ctrl_base_addr; | ^~~~~~~~~~~~~~ vim +/end_pci +1212 drivers/pci/controller/pcie-microchip-host.c 1198 1199 static int mc_check_for_parent_dma_range_handling(struct platform_device *pdev, 1200 struct mc_pcie *port) 1201 { 1202 struct device *dev = &pdev->dev; 1203 struct device_node *dn = dev->of_node; 1204 struct of_range_parser parser; 1205 struct of_range range; 1206 int num_parent_ranges = 0; 1207 int num_ranges = 0; 1208 struct inbound_windows ranges[MC_MAX_NUM_INBOUND_WINDOWS] = { 0 }; 1209 u64 start_axi = GENMASK(63, 0); 1210 u64 end_axi = 0; 1211 u64 start_pci = GENMASK(63, 0); > 1212 u64 end_pci = 0; 1213 s64 size; 1214 u64 window_size; 1215 int i; 1216 1217 /* find all dma-ranges */ 1218 if (of_pci_dma_range_parser_init(&parser, dn)) { 1219 dev_err(dev, "missing dma-ranges property\n"); 1220 return -EINVAL; 1221 } 1222 1223 for_each_of_range(&parser, &range) { 1224 if (num_ranges > MC_MAX_NUM_INBOUND_WINDOWS) { 1225 dev_err(dev, "too many inbound ranges; %d available tables\n", 1226 MC_MAX_NUM_INBOUND_WINDOWS); 1227 return -EINVAL; 1228 } 1229 ranges[num_ranges].axi_addr = range.cpu_addr; 1230 ranges[num_ranges].pci_addr = range.pci_addr; 1231 ranges[num_ranges].size = range.size; 1232 1233 num_ranges++; 1234 } 1235 1236 /* 1237 * check for one level up; will need to adjust 1238 * address translation tables for these 1239 */ 1240 dn = of_get_parent(dn); 1241 if (dn) { 1242 of_pci_dma_range_parser_init(&parser, dn); 1243 1244 for_each_of_range(&parser, &range) { 1245 if (num_parent_ranges > MC_MAX_NUM_INBOUND_WINDOWS) { 1246 dev_err(dev, "too many parent inbound ranges; %d available tables\n", 1247 MC_MAX_NUM_INBOUND_WINDOWS); 1248 return -EINVAL; 1249 } 1250 ranges[num_parent_ranges].axi_addr = range.pci_addr; 1251 num_parent_ranges++; 1252 } 1253 } 1254 1255 if (num_parent_ranges) { 1256 if (num_ranges != num_parent_ranges) { 1257 dev_err(dev, "num parent inbound ranges must be 0 or match num inbound ranges\n"); 1258 return -EINVAL; 1259 } 1260 } 1261 1262 /* merge ranges */ 1263 for (i = 0; i < num_ranges; i++) { 1264 struct inbound_windows *range = &ranges[i]; 1265 1266 if (range->axi_addr < start_axi) { 1267 start_axi = range->axi_addr; 1268 start_pci = range->pci_addr; 1269 } 1270 1271 if (range->axi_addr + range->size > end_axi) { 1272 end_axi = range->axi_addr + range->size; 1273 end_pci = range->pci_addr + range->size; 1274 } 1275 } 1276 1277 /* move starts back as far as possible */ 1278 start_axi &= MC_ATT_MASK; 1279 start_pci &= MC_ATT_MASK; 1280 1281 /* adjust size to take account of that change */ 1282 size = end_axi - start_axi; 1283 1284 /* may need to adjust size up to the next largest power of 2 */ 1285 if (size < 1ull << ilog2(size)) 1286 size = 1ull << (ilog2(size) + 1); 1287 1288 window_size = 1ull << (ilog2(size) - 1); 1289 1290 /* divide merged range into windows */ 1291 i = 0; 1292 while (size > 0 && i < MC_MAX_NUM_INBOUND_WINDOWS) { 1293 port->inbound_windows[i].axi_addr = start_axi; 1294 port->inbound_windows[i].pci_addr = start_pci; 1295 port->inbound_windows[i].size = window_size; 1296 1297 size -= window_size; 1298 start_axi += window_size; 1299 start_pci += window_size; 1300 i++; 1301 port->num_inbound_windows = i; 1302 } 1303 1304 if (size < 0) { 1305 dev_err(dev, "insufficient windows to map inbound ranges\n"); 1306 return -EINVAL; 1307 } 1308 1309 return 0; 1310 } 1311
Hi, I love your patch! Perhaps something to improve: [auto build test WARNING on 3c1f24109dfc4fb1a3730ed237e50183c6bb26b3] url: https://github.com/intel-lab-lkp/linux/commits/daire-mcnamara-microchip-com/PCI-microchip-Partition-address-translations/20221116-220208 base: 3c1f24109dfc4fb1a3730ed237e50183c6bb26b3 patch link: https://lore.kernel.org/r/20221116135504.258687-9-daire.mcnamara%40microchip.com patch subject: [PATCH v1 8/9] PCI: microchip: Partition inbound address translation config: powerpc-allyesconfig compiler: powerpc-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/41006bdc423e557398463818442a7607a8111347 git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review daire-mcnamara-microchip-com/PCI-microchip-Partition-address-translations/20221116-220208 git checkout 41006bdc423e557398463818442a7607a8111347 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash drivers/pci/controller/ If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): In file included from include/linux/printk.h:566, from include/linux/kernel.h:29, from include/linux/clk.h:13, from drivers/pci/controller/pcie-microchip-host.c:10: drivers/pci/controller/pcie-microchip-host.c: In function 'mc_pcie_setup_inbound_ranges': >> drivers/pci/controller/pcie-microchip-host.c:990:37: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'phys_addr_t' {aka 'unsigned int'} [-Wformat=] 990 | dev_dbg(&pdev->dev, "0x%010llx..0x%010llx -> 0x%010llx\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dynamic_debug.h:223:29: note: in definition of macro '__dynamic_func_call_cls' 223 | func(&id, ##__VA_ARGS__); \ | ^~~~~~~~~~~ include/linux/dynamic_debug.h:249:9: note: in expansion of macro '_dynamic_func_call_cls' 249 | _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__) | ^~~~~~~~~~~~~~~~~~~~~~ include/linux/dynamic_debug.h:272:9: note: in expansion of macro '_dynamic_func_call' 272 | _dynamic_func_call(fmt, __dynamic_dev_dbg, \ | ^~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:155:9: note: in expansion of macro 'dynamic_dev_dbg' 155 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~~~~~~~~~ include/linux/dev_printk.h:155:30: note: in expansion of macro 'dev_fmt' 155 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/pci/controller/pcie-microchip-host.c:990:17: note: in expansion of macro 'dev_dbg' 990 | dev_dbg(&pdev->dev, "0x%010llx..0x%010llx -> 0x%010llx\n", | ^~~~~~~ drivers/pci/controller/pcie-microchip-host.c:990:46: note: format string is defined here 990 | dev_dbg(&pdev->dev, "0x%010llx..0x%010llx -> 0x%010llx\n", | ~~~~~~^ | | | long long unsigned int | %010x drivers/pci/controller/pcie-microchip-host.c:990:37: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 6 has type 'phys_addr_t' {aka 'unsigned int'} [-Wformat=] 990 | dev_dbg(&pdev->dev, "0x%010llx..0x%010llx -> 0x%010llx\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dynamic_debug.h:223:29: note: in definition of macro '__dynamic_func_call_cls' 223 | func(&id, ##__VA_ARGS__); \ | ^~~~~~~~~~~ include/linux/dynamic_debug.h:249:9: note: in expansion of macro '_dynamic_func_call_cls' 249 | _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__) | ^~~~~~~~~~~~~~~~~~~~~~ include/linux/dynamic_debug.h:272:9: note: in expansion of macro '_dynamic_func_call' 272 | _dynamic_func_call(fmt, __dynamic_dev_dbg, \ | ^~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:155:9: note: in expansion of macro 'dynamic_dev_dbg' 155 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~~~~~~~~~ include/linux/dev_printk.h:155:30: note: in expansion of macro 'dev_fmt' 155 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/pci/controller/pcie-microchip-host.c:990:17: note: in expansion of macro 'dev_dbg' 990 | dev_dbg(&pdev->dev, "0x%010llx..0x%010llx -> 0x%010llx\n", | ^~~~~~~ drivers/pci/controller/pcie-microchip-host.c:990:70: note: format string is defined here 990 | dev_dbg(&pdev->dev, "0x%010llx..0x%010llx -> 0x%010llx\n", | ~~~~~~^ | | | long long unsigned int | %010x In file included from include/linux/bitops.h:6, from include/linux/kernel.h:22: drivers/pci/controller/pcie-microchip-host.c: In function 'mc_check_for_parent_dma_range_handling': include/linux/bits.h:36:18: warning: right shift count is negative [-Wshift-count-negative] 36 | (~UL(0) >> (BITS_PER_LONG - 1 - (h)))) | ^~ include/linux/bits.h:38:38: note: in expansion of macro '__GENMASK' 38 | (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l)) | ^~~~~~~~~ drivers/pci/controller/pcie-microchip-host.c:1209:25: note: in expansion of macro 'GENMASK' 1209 | u64 start_axi = GENMASK(63, 0); | ^~~~~~~ include/linux/bits.h:36:18: warning: right shift count is negative [-Wshift-count-negative] 36 | (~UL(0) >> (BITS_PER_LONG - 1 - (h)))) | ^~ include/linux/bits.h:38:38: note: in expansion of macro '__GENMASK' 38 | (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l)) | ^~~~~~~~~ drivers/pci/controller/pcie-microchip-host.c:1211:25: note: in expansion of macro 'GENMASK' 1211 | u64 start_pci = GENMASK(63, 0); | ^~~~~~~ include/linux/bits.h:36:18: warning: right shift count is negative [-Wshift-count-negative] 36 | (~UL(0) >> (BITS_PER_LONG - 1 - (h)))) | ^~ include/linux/bits.h:38:38: note: in expansion of macro '__GENMASK' 38 | (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l)) | ^~~~~~~~~ drivers/pci/controller/pcie-microchip-host.c:26:49: note: in expansion of macro 'GENMASK' 26 | #define MC_ATT_MASK GENMASK(63, 31) | ^~~~~~~ drivers/pci/controller/pcie-microchip-host.c:1278:22: note: in expansion of macro 'MC_ATT_MASK' 1278 | start_axi &= MC_ATT_MASK; | ^~~~~~~~~~~ include/linux/bits.h:36:18: warning: right shift count is negative [-Wshift-count-negative] 36 | (~UL(0) >> (BITS_PER_LONG - 1 - (h)))) | ^~ include/linux/bits.h:38:38: note: in expansion of macro '__GENMASK' 38 | (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l)) | ^~~~~~~~~ drivers/pci/controller/pcie-microchip-host.c:26:49: note: in expansion of macro 'GENMASK' 26 | #define MC_ATT_MASK GENMASK(63, 31) | ^~~~~~~ drivers/pci/controller/pcie-microchip-host.c:1279:22: note: in expansion of macro 'MC_ATT_MASK' 1279 | start_pci &= MC_ATT_MASK; | ^~~~~~~~~~~ drivers/pci/controller/pcie-microchip-host.c:1212:13: warning: variable 'end_pci' set but not used [-Wunused-but-set-variable] 1212 | u64 end_pci = 0; vim +990 drivers/pci/controller/pcie-microchip-host.c 956 957 static int mc_pcie_setup_inbound_ranges(struct platform_device *pdev, struct mc_pcie *port) 958 { 959 void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; 960 phys_addr_t pcie_addr; 961 phys_addr_t axi_addr; 962 u32 atr_size; 963 u32 val; 964 int i; 965 966 for (i = 0; i < port->num_inbound_windows; i++) { 967 atr_size = ilog2(port->inbound_windows[i].size) - 1; 968 atr_size &= GENMASK(5, 0); 969 970 pcie_addr = port->inbound_windows[i].pci_addr; 971 972 val = lower_32_bits(pcie_addr) & GENMASK(31, 12); 973 val |= (atr_size << ATR_SIZE_SHIFT); 974 val |= ATR_IMPL_ENABLE; 975 writel(val, bridge_base_addr + 976 ATR0_PCIE_WIN0_SRCADDR_PARAM + (i * ATR_WINDOW_DESC_SIZE)); 977 writel(upper_32_bits(pcie_addr), bridge_base_addr + 978 ATR0_PCIE_WIN0_SRC_ADDR + (i * ATR_WINDOW_DESC_SIZE)); 979 980 axi_addr = port->inbound_windows[i].axi_addr; 981 982 writel(lower_32_bits(axi_addr), bridge_base_addr + 983 ATR0_PCIE_WIN0_TRSL_ADDR_LSB + (i * ATR_WINDOW_DESC_SIZE)); 984 writel(upper_32_bits(axi_addr), bridge_base_addr + 985 ATR0_PCIE_WIN0_TRSL_ADDR_UDW + (i * ATR_WINDOW_DESC_SIZE)); 986 987 writel(TRSL_ID_AXI4_MASTER_0, bridge_base_addr + 988 ATR0_PCIE_WIN0_TRSL_PARAM + (i * ATR_WINDOW_DESC_SIZE)); 989 > 990 dev_dbg(&pdev->dev, "0x%010llx..0x%010llx -> 0x%010llx\n", 991 pcie_addr, pcie_addr + port->inbound_windows[i].size - 1, axi_addr); 992 } 993 994 return 0; 995 } 996
On Wed, Nov 16, 2022 at 01:55:03PM +0000, daire.mcnamara@microchip.com wrote: > From: Daire McNamara <daire.mcnamara@microchip.com> > > On Microchip PolarFire SoC the PCIe rootport is behind a set of fabric > inter connect (fic) busses that encapsulate busses like ABP/AHP, AXI-S > and AXI-M. Depending on which fic(s) the rootport is wired through to > cpu space, the rootport driver needs to take account of the address > translation done by a parent (e.g. fabric) node before setting up its > own inbound address translation tables from attached devices. > > Parse the dma-range properties to determine how much address translation > to perform in the root port and how much is being provided by the > fabric. > > Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com> > Signed-off-by: Conor Dooley <conor.dooley@microchip.com> > --- > drivers/pci/controller/pcie-microchip-host.c | 184 ++++++++++++++++++- > 1 file changed, 178 insertions(+), 6 deletions(-) > > diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c > index 62f8c5edfd0e..a90a0a675f14 100644 > --- a/drivers/pci/controller/pcie-microchip-host.c > +++ b/drivers/pci/controller/pcie-microchip-host.c > @@ -940,6 +954,46 @@ static int mc_pcie_init_irq_domains(struct mc_pcie *port) > return mc_allocate_msi_domains(port); > } > > +static int mc_pcie_setup_inbound_ranges(struct platform_device *pdev, struct mc_pcie *port) > +{ > + void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; > + phys_addr_t pcie_addr; > + phys_addr_t axi_addr; > + u32 atr_size; > + u32 val; > + int i; > + > + for (i = 0; i < port->num_inbound_windows; i++) { > + atr_size = ilog2(port->inbound_windows[i].size) - 1; > + atr_size &= GENMASK(5, 0); > + > + pcie_addr = port->inbound_windows[i].pci_addr; > + > + val = lower_32_bits(pcie_addr) & GENMASK(31, 12); > + val |= (atr_size << ATR_SIZE_SHIFT); > + val |= ATR_IMPL_ENABLE; > + writel(val, bridge_base_addr + > + ATR0_PCIE_WIN0_SRCADDR_PARAM + (i * ATR_WINDOW_DESC_SIZE)); > + writel(upper_32_bits(pcie_addr), bridge_base_addr + > + ATR0_PCIE_WIN0_SRC_ADDR + (i * ATR_WINDOW_DESC_SIZE)); > + > + axi_addr = port->inbound_windows[i].axi_addr; > + > + writel(lower_32_bits(axi_addr), bridge_base_addr + > + ATR0_PCIE_WIN0_TRSL_ADDR_LSB + (i * ATR_WINDOW_DESC_SIZE)); > + writel(upper_32_bits(axi_addr), bridge_base_addr + > + ATR0_PCIE_WIN0_TRSL_ADDR_UDW + (i * ATR_WINDOW_DESC_SIZE)); > + > + writel(TRSL_ID_AXI4_MASTER_0, bridge_base_addr + > + ATR0_PCIE_WIN0_TRSL_PARAM + (i * ATR_WINDOW_DESC_SIZE)); > + > + dev_dbg(&pdev->dev, "0x%010llx..0x%010llx -> 0x%010llx\n", > + pcie_addr, pcie_addr + port->inbound_windows[i].size - 1, axi_addr); If you're going to leave the dbg print in, can you make it more verbose so that the log message stands on its own? Other than that, I made most of my comments before submission so LGTM.. Reviewed-by: Conor Dooley <conor.dooley@microchip.com> Thanks, Conor.
diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c index 62f8c5edfd0e..a90a0a675f14 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/pcie-microchip-host.c @@ -22,6 +22,9 @@ /* Number of MSI IRQs */ #define MC_MAX_NUM_MSI_IRQS 32 +#define MC_MAX_NUM_INBOUND_WINDOWS 8 +#define MC_ATT_MASK GENMASK(63, 31) + /* PCIe Bridge Phy and Controller Phy offsets */ #define MC_PCIE1_BRIDGE_ADDR 0x00008000u #define MC_PCIE1_CTRL_ADDR 0x0000a000u @@ -86,10 +89,13 @@ #define ISTATUS_MSI 0x194 #define ATR_WINDOW_DESC_SIZE 32 -#define ATR_PCIE_ATR_SIZE 0x25 #define ATR_SIZE_SHIFT 1 #define ATR_IMPL_ENABLE 1 +#define ATR_PCIE_WIN0_SRCADDR 0x80000000 +#define ATR_PCIE_ATR_SIZE (512 * 1024 * 1024ul) +#define ATR_PCIE_NUM_WINDOWS 8 + /* PCIe Master table init defines */ #define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u #define ATR0_PCIE_WIN0_SRC_ADDR 0x604u @@ -276,6 +282,12 @@ struct mc_msi { DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS); }; +struct inbound_windows { + u64 axi_addr; + u64 pci_addr; + u64 size; +}; + struct mc_pcie { void __iomem *axi_base_addr; struct device *dev; @@ -284,6 +296,8 @@ struct mc_pcie { raw_spinlock_t lock; struct mc_msi msi; u64 outbound_range_offset; + u32 num_inbound_windows; + struct inbound_windows inbound_windows[MC_MAX_NUM_INBOUND_WINDOWS]; }; struct cause { @@ -940,6 +954,46 @@ static int mc_pcie_init_irq_domains(struct mc_pcie *port) return mc_allocate_msi_domains(port); } +static int mc_pcie_setup_inbound_ranges(struct platform_device *pdev, struct mc_pcie *port) +{ + void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + phys_addr_t pcie_addr; + phys_addr_t axi_addr; + u32 atr_size; + u32 val; + int i; + + for (i = 0; i < port->num_inbound_windows; i++) { + atr_size = ilog2(port->inbound_windows[i].size) - 1; + atr_size &= GENMASK(5, 0); + + pcie_addr = port->inbound_windows[i].pci_addr; + + val = lower_32_bits(pcie_addr) & GENMASK(31, 12); + val |= (atr_size << ATR_SIZE_SHIFT); + val |= ATR_IMPL_ENABLE; + writel(val, bridge_base_addr + + ATR0_PCIE_WIN0_SRCADDR_PARAM + (i * ATR_WINDOW_DESC_SIZE)); + writel(upper_32_bits(pcie_addr), bridge_base_addr + + ATR0_PCIE_WIN0_SRC_ADDR + (i * ATR_WINDOW_DESC_SIZE)); + + axi_addr = port->inbound_windows[i].axi_addr; + + writel(lower_32_bits(axi_addr), bridge_base_addr + + ATR0_PCIE_WIN0_TRSL_ADDR_LSB + (i * ATR_WINDOW_DESC_SIZE)); + writel(upper_32_bits(axi_addr), bridge_base_addr + + ATR0_PCIE_WIN0_TRSL_ADDR_UDW + (i * ATR_WINDOW_DESC_SIZE)); + + writel(TRSL_ID_AXI4_MASTER_0, bridge_base_addr + + ATR0_PCIE_WIN0_TRSL_PARAM + (i * ATR_WINDOW_DESC_SIZE)); + + dev_dbg(&pdev->dev, "0x%010llx..0x%010llx -> 0x%010llx\n", + pcie_addr, pcie_addr + port->inbound_windows[i].size - 1, axi_addr); + } + + return 0; +} + static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, phys_addr_t axi_addr, phys_addr_t pci_addr, size_t size) @@ -971,11 +1025,6 @@ static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, val = upper_32_bits(pci_addr); writel(val, bridge_base_addr + (index * ATR_WINDOW_DESC_SIZE) + ATR0_AXI4_SLV0_TRSL_ADDR_UDW); - - val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); - val |= (ATR_PCIE_ATR_SIZE << ATR_SIZE_SHIFT); - writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); - writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); } static int mc_pcie_setup_windows(struct platform_device *pdev, @@ -1147,6 +1196,119 @@ static int mc_check_for_parent_range_handling(struct platform_device *pdev, stru return 0; } +static int mc_check_for_parent_dma_range_handling(struct platform_device *pdev, + struct mc_pcie *port) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node; + struct of_range_parser parser; + struct of_range range; + int num_parent_ranges = 0; + int num_ranges = 0; + struct inbound_windows ranges[MC_MAX_NUM_INBOUND_WINDOWS] = { 0 }; + u64 start_axi = GENMASK(63, 0); + u64 end_axi = 0; + u64 start_pci = GENMASK(63, 0); + u64 end_pci = 0; + s64 size; + u64 window_size; + int i; + + /* find all dma-ranges */ + if (of_pci_dma_range_parser_init(&parser, dn)) { + dev_err(dev, "missing dma-ranges property\n"); + return -EINVAL; + } + + for_each_of_range(&parser, &range) { + if (num_ranges > MC_MAX_NUM_INBOUND_WINDOWS) { + dev_err(dev, "too many inbound ranges; %d available tables\n", + MC_MAX_NUM_INBOUND_WINDOWS); + return -EINVAL; + } + ranges[num_ranges].axi_addr = range.cpu_addr; + ranges[num_ranges].pci_addr = range.pci_addr; + ranges[num_ranges].size = range.size; + + num_ranges++; + } + + /* + * check for one level up; will need to adjust + * address translation tables for these + */ + dn = of_get_parent(dn); + if (dn) { + of_pci_dma_range_parser_init(&parser, dn); + + for_each_of_range(&parser, &range) { + if (num_parent_ranges > MC_MAX_NUM_INBOUND_WINDOWS) { + dev_err(dev, "too many parent inbound ranges; %d available tables\n", + MC_MAX_NUM_INBOUND_WINDOWS); + return -EINVAL; + } + ranges[num_parent_ranges].axi_addr = range.pci_addr; + num_parent_ranges++; + } + } + + if (num_parent_ranges) { + if (num_ranges != num_parent_ranges) { + dev_err(dev, "num parent inbound ranges must be 0 or match num inbound ranges\n"); + return -EINVAL; + } + } + + /* merge ranges */ + for (i = 0; i < num_ranges; i++) { + struct inbound_windows *range = &ranges[i]; + + if (range->axi_addr < start_axi) { + start_axi = range->axi_addr; + start_pci = range->pci_addr; + } + + if (range->axi_addr + range->size > end_axi) { + end_axi = range->axi_addr + range->size; + end_pci = range->pci_addr + range->size; + } + } + + /* move starts back as far as possible */ + start_axi &= MC_ATT_MASK; + start_pci &= MC_ATT_MASK; + + /* adjust size to take account of that change */ + size = end_axi - start_axi; + + /* may need to adjust size up to the next largest power of 2 */ + if (size < 1ull << ilog2(size)) + size = 1ull << (ilog2(size) + 1); + + window_size = 1ull << (ilog2(size) - 1); + + /* divide merged range into windows */ + i = 0; + while (size > 0 && i < MC_MAX_NUM_INBOUND_WINDOWS) { + port->inbound_windows[i].axi_addr = start_axi; + port->inbound_windows[i].pci_addr = start_pci; + port->inbound_windows[i].size = window_size; + + size -= window_size; + start_axi += window_size; + start_pci += window_size; + i++; + port->num_inbound_windows = i; + } + + if (size < 0) { + dev_err(dev, "insufficient windows to map inbound ranges\n"); + return -EINVAL; + } + + return 0; +} + static int mc_platform_init(struct pci_config_window *cfg) { struct device *dev = cfg->parent; @@ -1164,6 +1326,11 @@ static int mc_platform_init(struct pci_config_window *cfg) if (ret) return ret; + /* and similarly, check for inbound address translation */ + ret = mc_check_for_parent_dma_range_handling(pdev, port); + if (ret) + return ret; + /* Configure address translation table 0 for PCIe config space */ mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start - port->outbound_range_offset, cfg->res.start - port->outbound_range_offset, @@ -1177,6 +1344,11 @@ static int mc_platform_init(struct pci_config_window *cfg) if (ret) return ret; + /* configure inbound translation tables */ + ret = mc_pcie_setup_inbound_ranges(pdev, port); + if (ret) + return ret; + /* address translation is up; safe to enable interrupts */ ret = mc_init_interrupts(pdev, port); if (ret)