2021-06-10 07:41:34 +01:00
// SPDX-License-Identifier: GPL-2.0+
/*
* ACPI quirks for Tegra194 PCIe host controller
*
* Copyright ( C ) 2021 NVIDIA Corporation .
*
* Author : Vidya Sagar < vidyas @ nvidia . com >
*/
# include <linux/pci.h>
# include <linux/pci-acpi.h>
# include <linux/pci-ecam.h>
# include "pcie-designware.h"
struct tegra194_pcie_ecam {
void __iomem * config_base ;
void __iomem * iatu_base ;
void __iomem * dbi_base ;
} ;
static int tegra194_acpi_init ( struct pci_config_window * cfg )
{
struct device * dev = cfg - > parent ;
struct tegra194_pcie_ecam * pcie_ecam ;
pcie_ecam = devm_kzalloc ( dev , sizeof ( * pcie_ecam ) , GFP_KERNEL ) ;
if ( ! pcie_ecam )
return - ENOMEM ;
pcie_ecam - > config_base = cfg - > win ;
pcie_ecam - > iatu_base = cfg - > win + SZ_256K ;
pcie_ecam - > dbi_base = cfg - > win + SZ_512K ;
cfg - > priv = pcie_ecam ;
return 0 ;
}
static void atu_reg_write ( struct tegra194_pcie_ecam * pcie_ecam , int index ,
u32 val , u32 reg )
{
PCI: dwc: Simplify in/outbound iATU setup methods
Previously __dw_pcie_prog_outbound_atu() duplicated a lot of code between
the iatu_unroll_enabled version and the PCIE_ATU_VIEWPORT version:
__dw_pcie_prog_outbound_atu
if (iatu_unroll_enabled)
dw_pcie_prog_outbound_atu_unroll
dw_pcie_writel_ob_unroll(PCIE_ATU_UNR_LOWER_BASE, ...)
dw_pcie_writel_ob_unroll(PCIE_ATU_UNR_UPPER_BASE, ...)
...
return
dw_pcie_writel_dbi(PCIE_ATU_VIEWPORT, ...)
dw_pcie_writel_dbi(PCIE_ATU_LOWER_BASE, ...)
dw_pcie_writel_dbi(PCIE_ATU_UPPER_BASE, ...)
...
Unify those by pushing the unroll address computation and viewport
selection down into dw_pcie_writel_atu() so we can use the same
dw_pcie_writel_atu_ob() accessor for both paths:
__dw_pcie_prog_outbound_atu
dw_pcie_writel_atu_ob(PCIE_ATU_LOWER_BASE, ...)
dw_pcie_writel_atu
dw_pcie_select_atu # new
if (iatu_unroll_enabled)
return pci->atu_base + PCIE_ATU_UNROLL_BASE(...)
dw_pcie_writel_dbi(PCIE_ATU_VIEWPORT, ...)
return pci->atu_base
dw_pcie_write(base + reg)
dw_pcie_writel_atu_ob(PCIE_ATU_UPPER_BASE, ...)
...
In the non-unroll case, this does involve more MMIO writes to
PCIE_ATU_VIEWPORT, but it's mainly in initialization paths and the code
simplification is significant.
[bhelgaas: commit log, simplify dw_pcie_select_atu()]
Link: https://lore.kernel.org/r/20220624143947.8991-12-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2022-06-24 17:39:43 +03:00
u32 offset = PCIE_ATU_UNROLL_BASE ( PCIE_ATU_REGION_DIR_OB , index ) +
PCIE_ATU_VIEWPORT_BASE ;
2021-06-10 07:41:34 +01:00
writel ( val , pcie_ecam - > iatu_base + offset + reg ) ;
}
static void program_outbound_atu ( struct tegra194_pcie_ecam * pcie_ecam ,
int index , int type , u64 cpu_addr ,
u64 pci_addr , u64 size )
{
atu_reg_write ( pcie_ecam , index , lower_32_bits ( cpu_addr ) ,
PCIE_ATU_LOWER_BASE ) ;
atu_reg_write ( pcie_ecam , index , upper_32_bits ( cpu_addr ) ,
PCIE_ATU_UPPER_BASE ) ;
atu_reg_write ( pcie_ecam , index , lower_32_bits ( pci_addr ) ,
PCIE_ATU_LOWER_TARGET ) ;
atu_reg_write ( pcie_ecam , index , lower_32_bits ( cpu_addr + size - 1 ) ,
PCIE_ATU_LIMIT ) ;
atu_reg_write ( pcie_ecam , index , upper_32_bits ( pci_addr ) ,
PCIE_ATU_UPPER_TARGET ) ;
PCI: dwc: Simplify in/outbound iATU setup methods
Previously __dw_pcie_prog_outbound_atu() duplicated a lot of code between
the iatu_unroll_enabled version and the PCIE_ATU_VIEWPORT version:
__dw_pcie_prog_outbound_atu
if (iatu_unroll_enabled)
dw_pcie_prog_outbound_atu_unroll
dw_pcie_writel_ob_unroll(PCIE_ATU_UNR_LOWER_BASE, ...)
dw_pcie_writel_ob_unroll(PCIE_ATU_UNR_UPPER_BASE, ...)
...
return
dw_pcie_writel_dbi(PCIE_ATU_VIEWPORT, ...)
dw_pcie_writel_dbi(PCIE_ATU_LOWER_BASE, ...)
dw_pcie_writel_dbi(PCIE_ATU_UPPER_BASE, ...)
...
Unify those by pushing the unroll address computation and viewport
selection down into dw_pcie_writel_atu() so we can use the same
dw_pcie_writel_atu_ob() accessor for both paths:
__dw_pcie_prog_outbound_atu
dw_pcie_writel_atu_ob(PCIE_ATU_LOWER_BASE, ...)
dw_pcie_writel_atu
dw_pcie_select_atu # new
if (iatu_unroll_enabled)
return pci->atu_base + PCIE_ATU_UNROLL_BASE(...)
dw_pcie_writel_dbi(PCIE_ATU_VIEWPORT, ...)
return pci->atu_base
dw_pcie_write(base + reg)
dw_pcie_writel_atu_ob(PCIE_ATU_UPPER_BASE, ...)
...
In the non-unroll case, this does involve more MMIO writes to
PCIE_ATU_VIEWPORT, but it's mainly in initialization paths and the code
simplification is significant.
[bhelgaas: commit log, simplify dw_pcie_select_atu()]
Link: https://lore.kernel.org/r/20220624143947.8991-12-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2022-06-24 17:39:43 +03:00
atu_reg_write ( pcie_ecam , index , type , PCIE_ATU_REGION_CTRL1 ) ;
atu_reg_write ( pcie_ecam , index , PCIE_ATU_ENABLE , PCIE_ATU_REGION_CTRL2 ) ;
2021-06-10 07:41:34 +01:00
}
static void __iomem * tegra194_map_bus ( struct pci_bus * bus ,
unsigned int devfn , int where )
{
struct pci_config_window * cfg = bus - > sysdata ;
struct tegra194_pcie_ecam * pcie_ecam = cfg - > priv ;
u32 busdev ;
int type ;
if ( bus - > number < cfg - > busr . start | | bus - > number > cfg - > busr . end )
return NULL ;
if ( bus - > number = = cfg - > busr . start ) {
if ( PCI_SLOT ( devfn ) = = 0 )
return pcie_ecam - > dbi_base + where ;
else
return NULL ;
}
busdev = PCIE_ATU_BUS ( bus - > number ) | PCIE_ATU_DEV ( PCI_SLOT ( devfn ) ) |
PCIE_ATU_FUNC ( PCI_FUNC ( devfn ) ) ;
if ( bus - > parent - > number = = cfg - > busr . start ) {
if ( PCI_SLOT ( devfn ) = = 0 )
type = PCIE_ATU_TYPE_CFG0 ;
else
return NULL ;
} else {
type = PCIE_ATU_TYPE_CFG1 ;
}
program_outbound_atu ( pcie_ecam , 0 , type , cfg - > res . start , busdev ,
SZ_256K ) ;
return pcie_ecam - > config_base + where ;
}
const struct pci_ecam_ops tegra194_pcie_ops = {
. init = tegra194_acpi_init ,
. pci_ops = {
. map_bus = tegra194_map_bus ,
. read = pci_generic_config_read ,
. write = pci_generic_config_write ,
}
} ;