mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 17:59:32 +00:00
iommu/tegra-smmu: Changes for v4.3-rc1
A bunch of improvements by Russell King, along with a fix to restore display support when using the SMMU. This was due to the SMMU driver writing the wrong value of active TLB lines, effectively disabling the TLB and causing massive underflows on the display controller because of the latency introduced by the SMMU. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJVzfJEAAoJEN0jrNd/PrOh9+oP/0SQz9xOzQsTHD4yT9bPg84E sZU+xWbUhSU3ni1Hq1I4F1HIAlCdlMsXlB7c2wTpTE55lgeDlHh5olG7Dc+6cLi2 hAlgooMMS1TShLlcHwMDOyyvXrkjg2qLp4sy4fyPVUcYUeZIREd9RfoCEbbS/4nV IQV3Q99zXi1rRBILROTRdd/3AfIS1XYvOQyTIM5dzgB2Kc9MC+C2tjC2zbVg0yPK TFzAzTwwvagbcxvfl1y3NtYTU27VOoE7uDRGYNaXyZxbi0TwyIX+4sZvf9M84aED zuEJwi+V3AqMWDu6JrQXuw1WAN/602pXtPdjSMFCi/bBdlCTgq+tTCvsSMcABXZ5 Pyk1XUPuT0UvD7GMROZRaDYmMw2fVyjswf1rjb18szw7HUEfYIdDWiuU8g1g44AB 3SqM1F1bxdBZ52q9gFWpuOR06E8cC7lr/FPtduseUbF39N9n8JfFZEI6+w3uXdrj 7TCEULNLvFp+oNzexHrbd/jGhrkW4aK78HttQpwpq4GW7zGeco/EW3BXC2zN5aJc YIfR0wVT5UrsCp2poRwpZFKtkxZVqU5t4IoB7XbL32vh1GAZf408XzAeiqYIcwv5 537XpLg6cqGqqAw+G5TrVWS1oVYbPIiTLyHHzkEEnBlgwccSrzbDSaqzpruOMF84 EhB0Rcuh9kFs6JIxdLnz =N5EP -----END PGP SIGNATURE----- Merge tag 'tegra-for-4.3-iommu' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into arm/tegra iommu/tegra-smmu: Changes for v4.3-rc1 A bunch of improvements by Russell King, along with a fix to restore display support when using the SMMU. This was due to the SMMU driver writing the wrong value of active TLB lines, effectively disabling the TLB and causing massive underflows on the display controller because of the latency introduced by the SMMU.
This commit is contained in:
commit
54e466fde4
5 changed files with 204 additions and 181 deletions
|
@ -27,6 +27,7 @@ struct tegra_smmu {
|
|||
const struct tegra_smmu_soc *soc;
|
||||
|
||||
unsigned long pfn_mask;
|
||||
unsigned long tlb_mask;
|
||||
|
||||
unsigned long *asids;
|
||||
struct mutex lock;
|
||||
|
@ -40,8 +41,10 @@ struct tegra_smmu_as {
|
|||
struct iommu_domain domain;
|
||||
struct tegra_smmu *smmu;
|
||||
unsigned int use_count;
|
||||
struct page *count;
|
||||
u32 *count;
|
||||
struct page **pts;
|
||||
struct page *pd;
|
||||
dma_addr_t pd_dma;
|
||||
unsigned id;
|
||||
u32 attr;
|
||||
};
|
||||
|
@ -68,7 +71,8 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset)
|
|||
#define SMMU_TLB_CONFIG 0x14
|
||||
#define SMMU_TLB_CONFIG_HIT_UNDER_MISS (1 << 29)
|
||||
#define SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION (1 << 28)
|
||||
#define SMMU_TLB_CONFIG_ACTIVE_LINES(x) ((x) & 0x3f)
|
||||
#define SMMU_TLB_CONFIG_ACTIVE_LINES(smmu) \
|
||||
((smmu)->soc->num_tlb_lines & (smmu)->tlb_mask)
|
||||
|
||||
#define SMMU_PTC_CONFIG 0x18
|
||||
#define SMMU_PTC_CONFIG_ENABLE (1 << 29)
|
||||
|
@ -79,9 +83,9 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset)
|
|||
#define SMMU_PTB_ASID_VALUE(x) ((x) & 0x7f)
|
||||
|
||||
#define SMMU_PTB_DATA 0x020
|
||||
#define SMMU_PTB_DATA_VALUE(page, attr) (page_to_phys(page) >> 12 | (attr))
|
||||
#define SMMU_PTB_DATA_VALUE(dma, attr) ((dma) >> 12 | (attr))
|
||||
|
||||
#define SMMU_MK_PDE(page, attr) (page_to_phys(page) >> SMMU_PTE_SHIFT | (attr))
|
||||
#define SMMU_MK_PDE(dma, attr) ((dma) >> SMMU_PTE_SHIFT | (attr))
|
||||
|
||||
#define SMMU_TLB_FLUSH 0x030
|
||||
#define SMMU_TLB_FLUSH_VA_MATCH_ALL (0 << 0)
|
||||
|
@ -134,29 +138,49 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset)
|
|||
#define SMMU_PTE_ATTR (SMMU_PTE_READABLE | SMMU_PTE_WRITABLE | \
|
||||
SMMU_PTE_NONSECURE)
|
||||
|
||||
static inline void smmu_flush_ptc(struct tegra_smmu *smmu, struct page *page,
|
||||
static unsigned int iova_pd_index(unsigned long iova)
|
||||
{
|
||||
return (iova >> SMMU_PDE_SHIFT) & (SMMU_NUM_PDE - 1);
|
||||
}
|
||||
|
||||
static unsigned int iova_pt_index(unsigned long iova)
|
||||
{
|
||||
return (iova >> SMMU_PTE_SHIFT) & (SMMU_NUM_PTE - 1);
|
||||
}
|
||||
|
||||
static bool smmu_dma_addr_valid(struct tegra_smmu *smmu, dma_addr_t addr)
|
||||
{
|
||||
addr >>= 12;
|
||||
return (addr & smmu->pfn_mask) == addr;
|
||||
}
|
||||
|
||||
static dma_addr_t smmu_pde_to_dma(u32 pde)
|
||||
{
|
||||
return pde << 12;
|
||||
}
|
||||
|
||||
static void smmu_flush_ptc_all(struct tegra_smmu *smmu)
|
||||
{
|
||||
smmu_writel(smmu, SMMU_PTC_FLUSH_TYPE_ALL, SMMU_PTC_FLUSH);
|
||||
}
|
||||
|
||||
static inline void smmu_flush_ptc(struct tegra_smmu *smmu, dma_addr_t dma,
|
||||
unsigned long offset)
|
||||
{
|
||||
phys_addr_t phys = page ? page_to_phys(page) : 0;
|
||||
u32 value;
|
||||
|
||||
if (page) {
|
||||
offset &= ~(smmu->mc->soc->atom_size - 1);
|
||||
|
||||
if (smmu->mc->soc->num_address_bits > 32) {
|
||||
#ifdef CONFIG_PHYS_ADDR_T_64BIT
|
||||
value = (phys >> 32) & SMMU_PTC_FLUSH_HI_MASK;
|
||||
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
|
||||
value = (dma >> 32) & SMMU_PTC_FLUSH_HI_MASK;
|
||||
#else
|
||||
value = 0;
|
||||
#endif
|
||||
smmu_writel(smmu, value, SMMU_PTC_FLUSH_HI);
|
||||
}
|
||||
|
||||
value = (phys + offset) | SMMU_PTC_FLUSH_TYPE_ADR;
|
||||
} else {
|
||||
value = SMMU_PTC_FLUSH_TYPE_ALL;
|
||||
}
|
||||
|
||||
value = (dma + offset) | SMMU_PTC_FLUSH_TYPE_ADR;
|
||||
smmu_writel(smmu, value, SMMU_PTC_FLUSH);
|
||||
}
|
||||
|
||||
|
@ -236,8 +260,6 @@ static bool tegra_smmu_capable(enum iommu_cap cap)
|
|||
static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)
|
||||
{
|
||||
struct tegra_smmu_as *as;
|
||||
unsigned int i;
|
||||
uint32_t *pd;
|
||||
|
||||
if (type != IOMMU_DOMAIN_UNMANAGED)
|
||||
return NULL;
|
||||
|
@ -248,32 +270,26 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)
|
|||
|
||||
as->attr = SMMU_PD_READABLE | SMMU_PD_WRITABLE | SMMU_PD_NONSECURE;
|
||||
|
||||
as->pd = alloc_page(GFP_KERNEL | __GFP_DMA);
|
||||
as->pd = alloc_page(GFP_KERNEL | __GFP_DMA | __GFP_ZERO);
|
||||
if (!as->pd) {
|
||||
kfree(as);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
as->count = alloc_page(GFP_KERNEL);
|
||||
as->count = kcalloc(SMMU_NUM_PDE, sizeof(u32), GFP_KERNEL);
|
||||
if (!as->count) {
|
||||
__free_page(as->pd);
|
||||
kfree(as);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* clear PDEs */
|
||||
pd = page_address(as->pd);
|
||||
SetPageReserved(as->pd);
|
||||
|
||||
for (i = 0; i < SMMU_NUM_PDE; i++)
|
||||
pd[i] = 0;
|
||||
|
||||
/* clear PDE usage counters */
|
||||
pd = page_address(as->count);
|
||||
SetPageReserved(as->count);
|
||||
|
||||
for (i = 0; i < SMMU_NUM_PDE; i++)
|
||||
pd[i] = 0;
|
||||
as->pts = kcalloc(SMMU_NUM_PDE, sizeof(*as->pts), GFP_KERNEL);
|
||||
if (!as->pts) {
|
||||
kfree(as->count);
|
||||
__free_page(as->pd);
|
||||
kfree(as);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* setup aperture */
|
||||
as->domain.geometry.aperture_start = 0;
|
||||
|
@ -288,7 +304,6 @@ static void tegra_smmu_domain_free(struct iommu_domain *domain)
|
|||
struct tegra_smmu_as *as = to_smmu_as(domain);
|
||||
|
||||
/* TODO: free page directory and page tables */
|
||||
ClearPageReserved(as->pd);
|
||||
|
||||
kfree(as);
|
||||
}
|
||||
|
@ -376,16 +391,26 @@ static int tegra_smmu_as_prepare(struct tegra_smmu *smmu,
|
|||
return 0;
|
||||
}
|
||||
|
||||
as->pd_dma = dma_map_page(smmu->dev, as->pd, 0, SMMU_SIZE_PD,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(smmu->dev, as->pd_dma))
|
||||
return -ENOMEM;
|
||||
|
||||
/* We can't handle 64-bit DMA addresses */
|
||||
if (!smmu_dma_addr_valid(smmu, as->pd_dma)) {
|
||||
err = -ENOMEM;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
err = tegra_smmu_alloc_asid(smmu, &as->id);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto err_unmap;
|
||||
|
||||
smmu->soc->ops->flush_dcache(as->pd, 0, SMMU_SIZE_PD);
|
||||
smmu_flush_ptc(smmu, as->pd, 0);
|
||||
smmu_flush_ptc(smmu, as->pd_dma, 0);
|
||||
smmu_flush_tlb_asid(smmu, as->id);
|
||||
|
||||
smmu_writel(smmu, as->id & 0x7f, SMMU_PTB_ASID);
|
||||
value = SMMU_PTB_DATA_VALUE(as->pd, as->attr);
|
||||
value = SMMU_PTB_DATA_VALUE(as->pd_dma, as->attr);
|
||||
smmu_writel(smmu, value, SMMU_PTB_DATA);
|
||||
smmu_flush(smmu);
|
||||
|
||||
|
@ -393,6 +418,10 @@ static int tegra_smmu_as_prepare(struct tegra_smmu *smmu,
|
|||
as->use_count++;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unmap:
|
||||
dma_unmap_page(smmu->dev, as->pd_dma, SMMU_SIZE_PD, DMA_TO_DEVICE);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void tegra_smmu_as_unprepare(struct tegra_smmu *smmu,
|
||||
|
@ -402,6 +431,9 @@ static void tegra_smmu_as_unprepare(struct tegra_smmu *smmu,
|
|||
return;
|
||||
|
||||
tegra_smmu_free_asid(smmu, as->id);
|
||||
|
||||
dma_unmap_page(smmu->dev, as->pd_dma, SMMU_SIZE_PD, DMA_TO_DEVICE);
|
||||
|
||||
as->smmu = NULL;
|
||||
}
|
||||
|
||||
|
@ -465,96 +497,155 @@ static void tegra_smmu_detach_dev(struct iommu_domain *domain, struct device *de
|
|||
}
|
||||
}
|
||||
|
||||
static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,
|
||||
struct page **pagep)
|
||||
static void tegra_smmu_set_pde(struct tegra_smmu_as *as, unsigned long iova,
|
||||
u32 value)
|
||||
{
|
||||
u32 *pd = page_address(as->pd), *pt, *count;
|
||||
u32 pde = (iova >> SMMU_PDE_SHIFT) & 0x3ff;
|
||||
u32 pte = (iova >> SMMU_PTE_SHIFT) & 0x3ff;
|
||||
unsigned int pd_index = iova_pd_index(iova);
|
||||
struct tegra_smmu *smmu = as->smmu;
|
||||
struct page *page;
|
||||
unsigned int i;
|
||||
u32 *pd = page_address(as->pd);
|
||||
unsigned long offset = pd_index * sizeof(*pd);
|
||||
|
||||
if (pd[pde] == 0) {
|
||||
page = alloc_page(GFP_KERNEL | __GFP_DMA);
|
||||
/* Set the page directory entry first */
|
||||
pd[pd_index] = value;
|
||||
|
||||
/* The flush the page directory entry from caches */
|
||||
dma_sync_single_range_for_device(smmu->dev, as->pd_dma, offset,
|
||||
sizeof(*pd), DMA_TO_DEVICE);
|
||||
|
||||
/* And flush the iommu */
|
||||
smmu_flush_ptc(smmu, as->pd_dma, offset);
|
||||
smmu_flush_tlb_section(smmu, as->id, iova);
|
||||
smmu_flush(smmu);
|
||||
}
|
||||
|
||||
static u32 *tegra_smmu_pte_offset(struct page *pt_page, unsigned long iova)
|
||||
{
|
||||
u32 *pt = page_address(pt_page);
|
||||
|
||||
return pt + iova_pt_index(iova);
|
||||
}
|
||||
|
||||
static u32 *tegra_smmu_pte_lookup(struct tegra_smmu_as *as, unsigned long iova,
|
||||
dma_addr_t *dmap)
|
||||
{
|
||||
unsigned int pd_index = iova_pd_index(iova);
|
||||
struct page *pt_page;
|
||||
u32 *pd;
|
||||
|
||||
pt_page = as->pts[pd_index];
|
||||
if (!pt_page)
|
||||
return NULL;
|
||||
|
||||
pd = page_address(as->pd);
|
||||
*dmap = smmu_pde_to_dma(pd[pd_index]);
|
||||
|
||||
return tegra_smmu_pte_offset(pt_page, iova);
|
||||
}
|
||||
|
||||
static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,
|
||||
dma_addr_t *dmap)
|
||||
{
|
||||
unsigned int pde = iova_pd_index(iova);
|
||||
struct tegra_smmu *smmu = as->smmu;
|
||||
|
||||
if (!as->pts[pde]) {
|
||||
struct page *page;
|
||||
dma_addr_t dma;
|
||||
|
||||
page = alloc_page(GFP_KERNEL | __GFP_DMA | __GFP_ZERO);
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
pt = page_address(page);
|
||||
SetPageReserved(page);
|
||||
dma = dma_map_page(smmu->dev, page, 0, SMMU_SIZE_PT,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(smmu->dev, dma)) {
|
||||
__free_page(page);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < SMMU_NUM_PTE; i++)
|
||||
pt[i] = 0;
|
||||
if (!smmu_dma_addr_valid(smmu, dma)) {
|
||||
dma_unmap_page(smmu->dev, dma, SMMU_SIZE_PT,
|
||||
DMA_TO_DEVICE);
|
||||
__free_page(page);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
smmu->soc->ops->flush_dcache(page, 0, SMMU_SIZE_PT);
|
||||
as->pts[pde] = page;
|
||||
|
||||
pd[pde] = SMMU_MK_PDE(page, SMMU_PDE_ATTR | SMMU_PDE_NEXT);
|
||||
tegra_smmu_set_pde(as, iova, SMMU_MK_PDE(dma, SMMU_PDE_ATTR |
|
||||
SMMU_PDE_NEXT));
|
||||
|
||||
smmu->soc->ops->flush_dcache(as->pd, pde << 2, 4);
|
||||
smmu_flush_ptc(smmu, as->pd, pde << 2);
|
||||
smmu_flush_tlb_section(smmu, as->id, iova);
|
||||
smmu_flush(smmu);
|
||||
*dmap = dma;
|
||||
} else {
|
||||
page = pfn_to_page(pd[pde] & smmu->pfn_mask);
|
||||
pt = page_address(page);
|
||||
u32 *pd = page_address(as->pd);
|
||||
|
||||
*dmap = smmu_pde_to_dma(pd[pde]);
|
||||
}
|
||||
|
||||
*pagep = page;
|
||||
|
||||
/* Keep track of entries in this page table. */
|
||||
count = page_address(as->count);
|
||||
if (pt[pte] == 0)
|
||||
count[pde]++;
|
||||
|
||||
return &pt[pte];
|
||||
return tegra_smmu_pte_offset(as->pts[pde], iova);
|
||||
}
|
||||
|
||||
static void as_put_pte(struct tegra_smmu_as *as, dma_addr_t iova)
|
||||
static void tegra_smmu_pte_get_use(struct tegra_smmu_as *as, unsigned long iova)
|
||||
{
|
||||
u32 pde = (iova >> SMMU_PDE_SHIFT) & 0x3ff;
|
||||
u32 pte = (iova >> SMMU_PTE_SHIFT) & 0x3ff;
|
||||
u32 *count = page_address(as->count);
|
||||
u32 *pd = page_address(as->pd), *pt;
|
||||
struct page *page;
|
||||
unsigned int pd_index = iova_pd_index(iova);
|
||||
|
||||
page = pfn_to_page(pd[pde] & as->smmu->pfn_mask);
|
||||
pt = page_address(page);
|
||||
as->count[pd_index]++;
|
||||
}
|
||||
|
||||
static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova)
|
||||
{
|
||||
unsigned int pde = iova_pd_index(iova);
|
||||
struct page *page = as->pts[pde];
|
||||
|
||||
/*
|
||||
* When no entries in this page table are used anymore, return the
|
||||
* memory page to the system.
|
||||
*/
|
||||
if (pt[pte] != 0) {
|
||||
if (--count[pde] == 0) {
|
||||
ClearPageReserved(page);
|
||||
if (--as->count[pde] == 0) {
|
||||
struct tegra_smmu *smmu = as->smmu;
|
||||
u32 *pd = page_address(as->pd);
|
||||
dma_addr_t pte_dma = smmu_pde_to_dma(pd[pde]);
|
||||
|
||||
tegra_smmu_set_pde(as, iova, 0);
|
||||
|
||||
dma_unmap_page(smmu->dev, pte_dma, SMMU_SIZE_PT, DMA_TO_DEVICE);
|
||||
__free_page(page);
|
||||
pd[pde] = 0;
|
||||
as->pts[pde] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pt[pte] = 0;
|
||||
}
|
||||
static void tegra_smmu_set_pte(struct tegra_smmu_as *as, unsigned long iova,
|
||||
u32 *pte, dma_addr_t pte_dma, u32 val)
|
||||
{
|
||||
struct tegra_smmu *smmu = as->smmu;
|
||||
unsigned long offset = offset_in_page(pte);
|
||||
|
||||
*pte = val;
|
||||
|
||||
dma_sync_single_range_for_device(smmu->dev, pte_dma, offset,
|
||||
4, DMA_TO_DEVICE);
|
||||
smmu_flush_ptc(smmu, pte_dma, offset);
|
||||
smmu_flush_tlb_group(smmu, as->id, iova);
|
||||
smmu_flush(smmu);
|
||||
}
|
||||
|
||||
static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot)
|
||||
{
|
||||
struct tegra_smmu_as *as = to_smmu_as(domain);
|
||||
struct tegra_smmu *smmu = as->smmu;
|
||||
unsigned long offset;
|
||||
struct page *page;
|
||||
dma_addr_t pte_dma;
|
||||
u32 *pte;
|
||||
|
||||
pte = as_get_pte(as, iova, &page);
|
||||
pte = as_get_pte(as, iova, &pte_dma);
|
||||
if (!pte)
|
||||
return -ENOMEM;
|
||||
|
||||
*pte = __phys_to_pfn(paddr) | SMMU_PTE_ATTR;
|
||||
offset = offset_in_page(pte);
|
||||
/* If we aren't overwriting a pre-existing entry, increment use */
|
||||
if (*pte == 0)
|
||||
tegra_smmu_pte_get_use(as, iova);
|
||||
|
||||
smmu->soc->ops->flush_dcache(page, offset, 4);
|
||||
smmu_flush_ptc(smmu, page, offset);
|
||||
smmu_flush_tlb_group(smmu, as->id, iova);
|
||||
smmu_flush(smmu);
|
||||
tegra_smmu_set_pte(as, iova, pte, pte_dma,
|
||||
__phys_to_pfn(paddr) | SMMU_PTE_ATTR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -563,22 +654,15 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
|
|||
size_t size)
|
||||
{
|
||||
struct tegra_smmu_as *as = to_smmu_as(domain);
|
||||
struct tegra_smmu *smmu = as->smmu;
|
||||
unsigned long offset;
|
||||
struct page *page;
|
||||
dma_addr_t pte_dma;
|
||||
u32 *pte;
|
||||
|
||||
pte = as_get_pte(as, iova, &page);
|
||||
if (!pte)
|
||||
pte = tegra_smmu_pte_lookup(as, iova, &pte_dma);
|
||||
if (!pte || !*pte)
|
||||
return 0;
|
||||
|
||||
offset = offset_in_page(pte);
|
||||
as_put_pte(as, iova);
|
||||
|
||||
smmu->soc->ops->flush_dcache(page, offset, 4);
|
||||
smmu_flush_ptc(smmu, page, offset);
|
||||
smmu_flush_tlb_group(smmu, as->id, iova);
|
||||
smmu_flush(smmu);
|
||||
tegra_smmu_set_pte(as, iova, pte, pte_dma, 0);
|
||||
tegra_smmu_pte_put_use(as, iova);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
@ -587,11 +671,14 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain,
|
|||
dma_addr_t iova)
|
||||
{
|
||||
struct tegra_smmu_as *as = to_smmu_as(domain);
|
||||
struct page *page;
|
||||
unsigned long pfn;
|
||||
dma_addr_t pte_dma;
|
||||
u32 *pte;
|
||||
|
||||
pte = as_get_pte(as, iova, &page);
|
||||
pte = tegra_smmu_pte_lookup(as, iova, &pte_dma);
|
||||
if (!pte || !*pte)
|
||||
return 0;
|
||||
|
||||
pfn = *pte & as->smmu->pfn_mask;
|
||||
|
||||
return PFN_PHYS(pfn);
|
||||
|
@ -816,6 +903,9 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
|
|||
smmu->pfn_mask = BIT_MASK(mc->soc->num_address_bits - PAGE_SHIFT) - 1;
|
||||
dev_dbg(dev, "address bits: %u, PFN mask: %#lx\n",
|
||||
mc->soc->num_address_bits, smmu->pfn_mask);
|
||||
smmu->tlb_mask = (smmu->soc->num_tlb_lines << 1) - 1;
|
||||
dev_dbg(dev, "TLB lines: %u, mask: %#lx\n", smmu->soc->num_tlb_lines,
|
||||
smmu->tlb_mask);
|
||||
|
||||
value = SMMU_PTC_CONFIG_ENABLE | SMMU_PTC_CONFIG_INDEX_MAP(0x3f);
|
||||
|
||||
|
@ -825,14 +915,14 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
|
|||
smmu_writel(smmu, value, SMMU_PTC_CONFIG);
|
||||
|
||||
value = SMMU_TLB_CONFIG_HIT_UNDER_MISS |
|
||||
SMMU_TLB_CONFIG_ACTIVE_LINES(0x20);
|
||||
SMMU_TLB_CONFIG_ACTIVE_LINES(smmu);
|
||||
|
||||
if (soc->supports_round_robin_arbitration)
|
||||
value |= SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION;
|
||||
|
||||
smmu_writel(smmu, value, SMMU_TLB_CONFIG);
|
||||
|
||||
smmu_flush_ptc(smmu, NULL, 0);
|
||||
smmu_flush_ptc_all(smmu);
|
||||
smmu_flush_tlb(smmu);
|
||||
smmu_writel(smmu, SMMU_CONFIG_ENABLE, SMMU_CONFIG);
|
||||
smmu_flush(smmu);
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <dt-bindings/memory/tegra114-mc.h>
|
||||
|
||||
#include "mc.h"
|
||||
|
@ -914,20 +912,6 @@ static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
|
|||
{ .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 },
|
||||
};
|
||||
|
||||
static void tegra114_flush_dcache(struct page *page, unsigned long offset,
|
||||
size_t size)
|
||||
{
|
||||
phys_addr_t phys = page_to_phys(page) + offset;
|
||||
void *virt = page_address(page) + offset;
|
||||
|
||||
__cpuc_flush_dcache_area(virt, size);
|
||||
outer_flush_range(phys, phys + size);
|
||||
}
|
||||
|
||||
static const struct tegra_smmu_ops tegra114_smmu_ops = {
|
||||
.flush_dcache = tegra114_flush_dcache,
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_soc tegra114_smmu_soc = {
|
||||
.clients = tegra114_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra114_mc_clients),
|
||||
|
@ -935,8 +919,8 @@ static const struct tegra_smmu_soc tegra114_smmu_soc = {
|
|||
.num_swgroups = ARRAY_SIZE(tegra114_swgroups),
|
||||
.supports_round_robin_arbitration = false,
|
||||
.supports_request_limit = false,
|
||||
.num_tlb_lines = 32,
|
||||
.num_asids = 4,
|
||||
.ops = &tegra114_smmu_ops,
|
||||
};
|
||||
|
||||
const struct tegra_mc_soc tegra114_mc_soc = {
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <dt-bindings/memory/tegra124-mc.h>
|
||||
|
||||
#include "mc.h"
|
||||
|
@ -1002,20 +1000,6 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
|
|||
};
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_124_SOC
|
||||
static void tegra124_flush_dcache(struct page *page, unsigned long offset,
|
||||
size_t size)
|
||||
{
|
||||
phys_addr_t phys = page_to_phys(page) + offset;
|
||||
void *virt = page_address(page) + offset;
|
||||
|
||||
__cpuc_flush_dcache_area(virt, size);
|
||||
outer_flush_range(phys, phys + size);
|
||||
}
|
||||
|
||||
static const struct tegra_smmu_ops tegra124_smmu_ops = {
|
||||
.flush_dcache = tegra124_flush_dcache,
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_soc tegra124_smmu_soc = {
|
||||
.clients = tegra124_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra124_mc_clients),
|
||||
|
@ -1024,7 +1008,6 @@ static const struct tegra_smmu_soc tegra124_smmu_soc = {
|
|||
.supports_round_robin_arbitration = true,
|
||||
.supports_request_limit = true,
|
||||
.num_asids = 128,
|
||||
.ops = &tegra124_smmu_ops,
|
||||
};
|
||||
|
||||
const struct tegra_mc_soc tegra124_mc_soc = {
|
||||
|
@ -1039,18 +1022,6 @@ const struct tegra_mc_soc tegra124_mc_soc = {
|
|||
#endif /* CONFIG_ARCH_TEGRA_124_SOC */
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_132_SOC
|
||||
static void tegra132_flush_dcache(struct page *page, unsigned long offset,
|
||||
size_t size)
|
||||
{
|
||||
void *virt = page_address(page) + offset;
|
||||
|
||||
__flush_dcache_area(virt, size);
|
||||
}
|
||||
|
||||
static const struct tegra_smmu_ops tegra132_smmu_ops = {
|
||||
.flush_dcache = tegra132_flush_dcache,
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_soc tegra132_smmu_soc = {
|
||||
.clients = tegra124_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra124_mc_clients),
|
||||
|
@ -1058,8 +1029,8 @@ static const struct tegra_smmu_soc tegra132_smmu_soc = {
|
|||
.num_swgroups = ARRAY_SIZE(tegra124_swgroups),
|
||||
.supports_round_robin_arbitration = true,
|
||||
.supports_request_limit = true,
|
||||
.num_tlb_lines = 32,
|
||||
.num_asids = 128,
|
||||
.ops = &tegra132_smmu_ops,
|
||||
};
|
||||
|
||||
const struct tegra_mc_soc tegra132_mc_soc = {
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <dt-bindings/memory/tegra30-mc.h>
|
||||
|
||||
#include "mc.h"
|
||||
|
@ -936,20 +934,6 @@ static const struct tegra_smmu_swgroup tegra30_swgroups[] = {
|
|||
{ .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 },
|
||||
};
|
||||
|
||||
static void tegra30_flush_dcache(struct page *page, unsigned long offset,
|
||||
size_t size)
|
||||
{
|
||||
phys_addr_t phys = page_to_phys(page) + offset;
|
||||
void *virt = page_address(page) + offset;
|
||||
|
||||
__cpuc_flush_dcache_area(virt, size);
|
||||
outer_flush_range(phys, phys + size);
|
||||
}
|
||||
|
||||
static const struct tegra_smmu_ops tegra30_smmu_ops = {
|
||||
.flush_dcache = tegra30_flush_dcache,
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_soc tegra30_smmu_soc = {
|
||||
.clients = tegra30_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra30_mc_clients),
|
||||
|
@ -957,8 +941,8 @@ static const struct tegra_smmu_soc tegra30_smmu_soc = {
|
|||
.num_swgroups = ARRAY_SIZE(tegra30_swgroups),
|
||||
.supports_round_robin_arbitration = false,
|
||||
.supports_request_limit = false,
|
||||
.num_tlb_lines = 16,
|
||||
.num_asids = 4,
|
||||
.ops = &tegra30_smmu_ops,
|
||||
};
|
||||
|
||||
const struct tegra_mc_soc tegra30_mc_soc = {
|
||||
|
|
|
@ -51,11 +51,6 @@ struct tegra_smmu_swgroup {
|
|||
unsigned int reg;
|
||||
};
|
||||
|
||||
struct tegra_smmu_ops {
|
||||
void (*flush_dcache)(struct page *page, unsigned long offset,
|
||||
size_t size);
|
||||
};
|
||||
|
||||
struct tegra_smmu_soc {
|
||||
const struct tegra_mc_client *clients;
|
||||
unsigned int num_clients;
|
||||
|
@ -66,9 +61,8 @@ struct tegra_smmu_soc {
|
|||
bool supports_round_robin_arbitration;
|
||||
bool supports_request_limit;
|
||||
|
||||
unsigned int num_tlb_lines;
|
||||
unsigned int num_asids;
|
||||
|
||||
const struct tegra_smmu_ops *ops;
|
||||
};
|
||||
|
||||
struct tegra_mc;
|
||||
|
|
Loading…
Reference in a new issue