Commit c0703c12 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'iommu-updates-v3.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu

Pull IOMMU updates from Joerg Roedel:
 "This time the IOMMU updates contain a bunch of fixes and cleanups to
  various IOMMU drivers and the DMA debug code.  New features are the
  code for IRQ remapping support with the AMD IOMMU (preperation for
  that was already merged in the last release) and a debugfs interface
  to export some statistics in the NVidia Tegra IOMMU driver."

* tag 'iommu-updates-v3.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (27 commits)
  iommu/amd: Remove obsolete comment line
  dma-debug: Remove local BUS_NOTIFY_UNBOUND_DRIVER define
  iommu/amd: Fix possible use after free in get_irq_table()
  iommu/amd: Report irq remapping through IOMMU-API
  iommu/amd: Print message to system log when irq remapping is enabled
  iommu/irq: Use amd_iommu_irq_ops if supported
  iommu/amd: Make sure irq remapping still works on dma init failure
  iommu/amd: Add initialization routines for AMD interrupt remapping
  iommu/amd: Add call-back routine for HPET MSI
  iommu/amd: Implement MSI routines for interrupt remapping
  iommu/amd: Add IOAPIC remapping routines
  iommu/amd: Add routines to manage irq remapping tables
  iommu/amd: Add IRTE invalidation routine
  iommu/amd: Make sure IOMMU is not considered to translate itself
  iommu/amd: Split device table initialization into irq and dma part
  iommu/amd: Check if IOAPIC information is correct
  iommu/amd: Allocate data structures to keep track of irq remapping tables
  iommu/amd: Add slab-cache for irq remapping tables
  iommu/amd: Keep track of HPET and IOAPIC device ids
  iommu/amd: Fix features reporting
  ...
parents 0e51793e 00948725
/*
* IOMMU API for SMMU in Tegra30
*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MACH_SMMU_H
#define MACH_SMMU_H
enum smmu_hwgrp {
HWGRP_AFI,
HWGRP_AVPC,
HWGRP_DC,
HWGRP_DCB,
HWGRP_EPP,
HWGRP_G2,
HWGRP_HC,
HWGRP_HDA,
HWGRP_ISP,
HWGRP_MPE,
HWGRP_NV,
HWGRP_NV2,
HWGRP_PPCS,
HWGRP_SATA,
HWGRP_VDE,
HWGRP_VI,
HWGRP_COUNT,
HWGRP_END = ~0,
};
#define HWG_AFI (1 << HWGRP_AFI)
#define HWG_AVPC (1 << HWGRP_AVPC)
#define HWG_DC (1 << HWGRP_DC)
#define HWG_DCB (1 << HWGRP_DCB)
#define HWG_EPP (1 << HWGRP_EPP)
#define HWG_G2 (1 << HWGRP_G2)
#define HWG_HC (1 << HWGRP_HC)
#define HWG_HDA (1 << HWGRP_HDA)
#define HWG_ISP (1 << HWGRP_ISP)
#define HWG_MPE (1 << HWGRP_MPE)
#define HWG_NV (1 << HWGRP_NV)
#define HWG_NV2 (1 << HWGRP_NV2)
#define HWG_PPCS (1 << HWGRP_PPCS)
#define HWG_SATA (1 << HWGRP_SATA)
#define HWG_VDE (1 << HWGRP_VDE)
#define HWG_VI (1 << HWGRP_VI)
#endif /* MACH_SMMU_H */
......@@ -42,7 +42,7 @@ config AMD_IOMMU
select PCI_PRI
select PCI_PASID
select IOMMU_API
depends on X86_64 && PCI && ACPI
depends on X86_64 && PCI && ACPI && X86_IO_APIC
---help---
With this option you can enable support for AMD IOMMU hardware in
your system. An IOMMU is a hardware component which provides
......
This diff is collapsed.
This diff is collapsed.
......@@ -32,6 +32,14 @@ extern void amd_iommu_uninit_devices(void);
extern void amd_iommu_init_notifier(void);
extern void amd_iommu_init_api(void);
/* Needed for interrupt remapping */
extern int amd_iommu_supported(void);
extern int amd_iommu_prepare(void);
extern int amd_iommu_enable(void);
extern void amd_iommu_disable(void);
extern int amd_iommu_reenable(int);
extern int amd_iommu_enable_faulting(void);
/* IOMMUv2 specific functions */
struct iommu_domain;
......
......@@ -152,6 +152,7 @@
#define CMD_INV_DEV_ENTRY 0x02
#define CMD_INV_IOMMU_PAGES 0x03
#define CMD_INV_IOTLB_PAGES 0x04
#define CMD_INV_IRT 0x05
#define CMD_COMPLETE_PPR 0x07
#define CMD_INV_ALL 0x08
......@@ -175,6 +176,7 @@
#define DEV_ENTRY_EX 0x67
#define DEV_ENTRY_SYSMGT1 0x68
#define DEV_ENTRY_SYSMGT2 0x69
#define DEV_ENTRY_IRQ_TBL_EN 0x80
#define DEV_ENTRY_INIT_PASS 0xb8
#define DEV_ENTRY_EINT_PASS 0xb9
#define DEV_ENTRY_NMI_PASS 0xba
......@@ -183,6 +185,8 @@
#define DEV_ENTRY_MODE_MASK 0x07
#define DEV_ENTRY_MODE_SHIFT 0x09
#define MAX_DEV_TABLE_ENTRIES 0xffff
/* constants to configure the command buffer */
#define CMD_BUFFER_SIZE 8192
#define CMD_BUFFER_UNINITIALIZED 1
......@@ -255,7 +259,7 @@
#define PAGE_SIZE_ALIGN(address, pagesize) \
((address) & ~((pagesize) - 1))
/*
* Creates an IOMMU PTE for an address an a given pagesize
* Creates an IOMMU PTE for an address and a given pagesize
* The PTE has no permission bits set
* Pagesize is expected to be a power-of-two larger than 4096
*/
......@@ -334,6 +338,23 @@ extern bool amd_iommu_np_cache;
/* Only true if all IOMMUs support device IOTLBs */
extern bool amd_iommu_iotlb_sup;
#define MAX_IRQS_PER_TABLE 256
#define IRQ_TABLE_ALIGNMENT 128
struct irq_remap_table {
spinlock_t lock;
unsigned min_index;
u32 *table;
};
extern struct irq_remap_table **irq_lookup_table;
/* Interrupt remapping feature used? */
extern bool amd_iommu_irq_remap;
/* kmem_cache to get tables with 128 byte alignement */
extern struct kmem_cache *amd_iommu_irq_cache;
/*
* Make iterating over all IOMMUs easier
*/
......@@ -404,7 +425,7 @@ struct iommu_dev_data {
struct list_head dev_data_list; /* For global dev_data_list */
struct iommu_dev_data *alias_data;/* The alias dev_data */
struct protection_domain *domain; /* Domain the device is bound to */
atomic_t bind; /* Domain attach reverent count */
atomic_t bind; /* Domain attach reference count */
u16 devid; /* PCI Device ID */
bool iommu_v2; /* Device can make use of IOMMUv2 */
bool passthrough; /* Default for device is pt_domain */
......@@ -565,6 +586,16 @@ struct amd_iommu {
u32 stored_l2[0x83];
};
struct devid_map {
struct list_head list;
u8 id;
u16 devid;
};
/* Map HPET and IOAPIC ids to the devid used by the IOMMU */
extern struct list_head ioapic_map;
extern struct list_head hpet_map;
/*
* List with all IOMMUs in the system. This list is not locked because it is
* only written and read at driver initialization or suspend time
......@@ -678,6 +709,30 @@ static inline u16 calc_devid(u8 bus, u8 devfn)
return (((u16)bus) << 8) | devfn;
}
static inline int get_ioapic_devid(int id)
{
struct devid_map *entry;
list_for_each_entry(entry, &ioapic_map, list) {
if (entry->id == id)
return entry->devid;
}
return -EINVAL;
}
static inline int get_hpet_devid(int id)
{
struct devid_map *entry;
list_for_each_entry(entry, &hpet_map, list) {
if (entry->id == id)
return entry->devid;
}
return -EINVAL;
}
#ifdef CONFIG_AMD_IOMMU_STATS
struct __iommu_counter {
......
......@@ -840,8 +840,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain,
if (__exynos_sysmmu_disable(data)) {
dev_dbg(dev, "%s: Detached IOMMU with pgtable %#lx\n",
__func__, __pa(priv->pgtable));
list_del(&data->node);
INIT_LIST_HEAD(&data->node);
list_del_init(&data->node);
} else {
dev_dbg(dev, "%s: Detaching IOMMU with pgtable %#lx delayed",
......
......@@ -589,7 +589,9 @@ static void domain_update_iommu_coherency(struct dmar_domain *domain)
{
int i;
domain->iommu_coherency = 1;
i = find_first_bit(domain->iommu_bmp, g_num_of_iommus);
domain->iommu_coherency = i < g_num_of_iommus ? 1 : 0;
for_each_set_bit(i, domain->iommu_bmp, g_num_of_iommus) {
if (!ecap_coherent(g_iommus[i]->ecap)) {
......
......@@ -51,6 +51,11 @@ early_param("intremap", setup_irqremap);
void __init setup_irq_remapping_ops(void)
{
remap_ops = &intel_irq_remap_ops;
#ifdef CONFIG_AMD_IOMMU
if (amd_iommu_irq_ops.prepare() == 0)
remap_ops = &amd_iommu_irq_ops;
#endif
}
int irq_remapping_supported(void)
......
......@@ -82,6 +82,12 @@ struct irq_remap_ops {
};
extern struct irq_remap_ops intel_irq_remap_ops;
extern struct irq_remap_ops amd_iommu_irq_ops;
#else /* CONFIG_IRQ_REMAP */
#define irq_remapping_enabled 0
#define disable_irq_remap 1
#endif /* CONFIG_IRQ_REMAP */
......
......@@ -32,14 +32,55 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_iommu.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <asm/page.h>
#include <asm/cacheflush.h>
#include <mach/iomap.h>
#include <mach/smmu.h>
#include <mach/tegra-ahb.h>
enum smmu_hwgrp {
HWGRP_AFI,
HWGRP_AVPC,
HWGRP_DC,
HWGRP_DCB,
HWGRP_EPP,
HWGRP_G2,
HWGRP_HC,
HWGRP_HDA,
HWGRP_ISP,
HWGRP_MPE,
HWGRP_NV,
HWGRP_NV2,
HWGRP_PPCS,
HWGRP_SATA,
HWGRP_VDE,
HWGRP_VI,
HWGRP_COUNT,
HWGRP_END = ~0,
};
#define HWG_AFI (1 << HWGRP_AFI)
#define HWG_AVPC (1 << HWGRP_AVPC)
#define HWG_DC (1 << HWGRP_DC)
#define HWG_DCB (1 << HWGRP_DCB)
#define HWG_EPP (1 << HWGRP_EPP)
#define HWG_G2 (1 << HWGRP_G2)
#define HWG_HC (1 << HWGRP_HC)
#define HWG_HDA (1 << HWGRP_HDA)
#define HWG_ISP (1 << HWGRP_ISP)
#define HWG_MPE (1 << HWGRP_MPE)
#define HWG_NV (1 << HWGRP_NV)
#define HWG_NV2 (1 << HWGRP_NV2)
#define HWG_PPCS (1 << HWGRP_PPCS)
#define HWG_SATA (1 << HWGRP_SATA)
#define HWG_VDE (1 << HWGRP_VDE)
#define HWG_VI (1 << HWGRP_VI)
/* bitmap of the page sizes currently supported */
#define SMMU_IOMMU_PGSIZES (SZ_4K)
......@@ -47,16 +88,29 @@
#define SMMU_CONFIG_DISABLE 0
#define SMMU_CONFIG_ENABLE 1
#define SMMU_TLB_CONFIG 0x14
#define SMMU_TLB_CONFIG_STATS__MASK (1 << 31)
#define SMMU_TLB_CONFIG_STATS__ENABLE (1 << 31)
/* REVISIT: To support multiple MCs */
enum {
_MC = 0,
};
enum {
_TLB = 0,
_PTC,
};
#define SMMU_CACHE_CONFIG_BASE 0x14
#define __SMMU_CACHE_CONFIG(mc, cache) (SMMU_CACHE_CONFIG_BASE + 4 * cache)
#define SMMU_CACHE_CONFIG(cache) __SMMU_CACHE_CONFIG(_MC, cache)
#define SMMU_CACHE_CONFIG_STATS_SHIFT 31
#define SMMU_CACHE_CONFIG_STATS_ENABLE (1 << SMMU_CACHE_CONFIG_STATS_SHIFT)
#define SMMU_CACHE_CONFIG_STATS_TEST_SHIFT 30
#define SMMU_CACHE_CONFIG_STATS_TEST (1 << SMMU_CACHE_CONFIG_STATS_TEST_SHIFT)
#define SMMU_TLB_CONFIG_HIT_UNDER_MISS__ENABLE (1 << 29)
#define SMMU_TLB_CONFIG_ACTIVE_LINES__VALUE 0x10
#define SMMU_TLB_CONFIG_RESET_VAL 0x20000010
#define SMMU_PTC_CONFIG 0x18
#define SMMU_PTC_CONFIG_STATS__MASK (1 << 31)
#define SMMU_PTC_CONFIG_STATS__ENABLE (1 << 31)
#define SMMU_PTC_CONFIG_CACHE__ENABLE (1 << 29)
#define SMMU_PTC_CONFIG_INDEX_MAP__PATTERN 0x3f
#define SMMU_PTC_CONFIG_RESET_VAL 0x2000003f
......@@ -86,10 +140,10 @@
#define SMMU_ASID_SECURITY 0x38
#define SMMU_STATS_TLB_HIT_COUNT 0x1f0
#define SMMU_STATS_TLB_MISS_COUNT 0x1f4
#define SMMU_STATS_PTC_HIT_COUNT 0x1f8
#define SMMU_STATS_PTC_MISS_COUNT 0x1fc
#define SMMU_STATS_CACHE_COUNT_BASE 0x1f0
#define SMMU_STATS_CACHE_COUNT(mc, cache, hitmiss) \
(SMMU_STATS_CACHE_COUNT_BASE + 8 * cache + 4 * hitmiss)
#define SMMU_TRANSLATION_ENABLE_0 0x228
#define SMMU_TRANSLATION_ENABLE_1 0x22c
......@@ -231,6 +285,12 @@ struct smmu_as {
spinlock_t client_lock; /* for client list */
};
struct smmu_debugfs_info {
struct smmu_device *smmu;
int mc;
int cache;
};
/*
* Per SMMU device - IOMMU device
*/
......@@ -251,6 +311,9 @@ struct smmu_device {
unsigned long translation_enable_2;
unsigned long asid_security;
struct dentry *debugfs_root;
struct smmu_debugfs_info *debugfs_info;
struct device_node *ahb;
int num_as;
......@@ -412,8 +475,8 @@ static int smmu_setup_regs(struct smmu_device *smmu)
smmu_write(smmu, smmu->translation_enable_1, SMMU_TRANSLATION_ENABLE_1);
smmu_write(smmu, smmu->translation_enable_2, SMMU_TRANSLATION_ENABLE_2);
smmu_write(smmu, smmu->asid_security, SMMU_ASID_SECURITY);
smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_TLB_CONFIG);
smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_PTC_CONFIG);
smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_TLB));
smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_PTC));
smmu_flush_regs(smmu, 1);
......@@ -895,6 +958,175 @@ static struct iommu_ops smmu_iommu_ops = {
.pgsize_bitmap = SMMU_IOMMU_PGSIZES,
};
/* Should be in the order of enum */
static const char * const smmu_debugfs_mc[] = { "mc", };
static const char * const smmu_debugfs_cache[] = { "tlb", "ptc", };
static ssize_t smmu_debugfs_stats_write(struct file *file,
const char __user *buffer,
size_t count, loff_t *pos)
{
struct smmu_debugfs_info *info;
struct smmu_device *smmu;
struct dentry *dent;
int i;
enum {
_OFF = 0,
_ON,
_RESET,
};
const char * const command[] = {
[_OFF] = "off",
[_ON] = "on",
[_RESET] = "reset",
};
char str[] = "reset";
u32 val;
size_t offs;
count = min_t(size_t, count, sizeof(str));
if (copy_from_user(str, buffer, count))
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(command); i++)
if (strncmp(str, command[i],
strlen(command[i])) == 0)
break;
if (i == ARRAY_SIZE(command))
return -EINVAL;
dent = file->f_dentry;
info = dent->d_inode->i_private;
smmu = info->smmu;
offs = SMMU_CACHE_CONFIG(info->cache);
val = smmu_read(smmu, offs);
switch (i) {
case _OFF:
val &= ~SMMU_CACHE_CONFIG_STATS_ENABLE;
val &= ~SMMU_CACHE_CONFIG_STATS_TEST;
smmu_write(smmu, val, offs);
break;
case _ON:
val |= SMMU_CACHE_CONFIG_STATS_ENABLE;
val &= ~SMMU_CACHE_CONFIG_STATS_TEST;
smmu_write(smmu, val, offs);
break;
case _RESET:
val |= SMMU_CACHE_CONFIG_STATS_TEST;
smmu_write(smmu, val, offs);
val &= ~SMMU_CACHE_CONFIG_STATS_TEST;
smmu_write(smmu, val, offs);
break;
default:
BUG();
break;
}
dev_dbg(smmu->dev, "%s() %08x, %08x @%08x\n", __func__,
val, smmu_read(smmu, offs), offs);
return count;
}
static int smmu_debugfs_stats_show(struct seq_file *s, void *v)
{
struct smmu_debugfs_info *info;
struct smmu_device *smmu;
struct dentry *dent;
int i;
const char * const stats[] = { "hit", "miss", };
dent = d_find_alias(s->private);
info = dent->d_inode->i_private;
smmu = info->smmu;
for (i = 0; i < ARRAY_SIZE(stats); i++) {
u32 val;
size_t offs;
offs = SMMU_STATS_CACHE_COUNT(info->mc, info->cache, i);
val = smmu_read(smmu, offs);
seq_printf(s, "%s:%08x ", stats[i], val);
dev_dbg(smmu->dev, "%s() %s %08x @%08x\n", __func__,
stats[i], val, offs);
}
seq_printf(s, "\n");
return 0;
}
static int smmu_debugfs_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, smmu_debugfs_stats_show, inode);
}
static const struct file_operations smmu_debugfs_stats_fops = {
.open = smmu_debugfs_stats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = smmu_debugfs_stats_write,
};
static void smmu_debugfs_delete(struct smmu_device *smmu)
{
debugfs_remove_recursive(smmu->debugfs_root);
kfree(smmu->debugfs_info);
}
static void smmu_debugfs_create(struct smmu_device *smmu)
{
int i;
size_t bytes;
struct dentry *root;
bytes = ARRAY_SIZE(smmu_debugfs_mc) * ARRAY_SIZE(smmu_debugfs_cache) *
sizeof(*smmu->debugfs_info);
smmu->debugfs_info = kmalloc(bytes, GFP_KERNEL);
if (!smmu->debugfs_info)
return;
root = debugfs_create_dir(dev_name(smmu->dev), NULL);
if (!root)
goto err_out;
smmu->debugfs_root = root;
for (i = 0; i < ARRAY_SIZE(smmu_debugfs_mc); i++) {
int j;
struct dentry *mc;
mc = debugfs_create_dir(smmu_debugfs_mc[i], root);
if (!mc)
goto err_out;
for (j = 0; j < ARRAY_SIZE(smmu_debugfs_cache); j++) {
struct dentry *cache;
struct smmu_debugfs_info *info;
info = smmu->debugfs_info;
info += i * ARRAY_SIZE(smmu_debugfs_mc) + j;
info->smmu = smmu;
info->mc = i;
info->cache = j;
cache = debugfs_create_file(smmu_debugfs_cache[j],
S_IWUGO | S_IRUGO, mc,
(void *)info,
&smmu_debugfs_stats_fops);
if (!cache)
goto err_out;
}
}
return;
err_out:
smmu_debugfs_delete(smmu);
}
static int tegra_smmu_suspend(struct device *dev)
{
struct smmu_device *smmu = dev_get_drvdata(dev);
......@@ -999,6 +1231,7 @@ static int tegra_smmu_probe(struct platform_device *pdev)
if (!smmu->avp_vector_page)
return -ENOMEM;
smmu_debugfs_create(smmu);
smmu_handle = smmu;
return 0;
}
......@@ -1008,6 +1241,8 @@ static int tegra_smmu_remove(struct platform_device *pdev)
struct smmu_device *smmu = platform_get_drvdata(pdev);
int i;
smmu_debugfs_delete(smmu);
smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG);
for (i = 0; i < smmu->num_as; i++)
free_pdir(&smmu->as[i]);
......
......@@ -120,11 +120,6 @@ static const char *type2name[4] = { "single", "page",
static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE",
"DMA_FROM_DEVICE", "DMA_NONE" };
/* little merge helper - remove it after the merge window */
#ifndef BUS_NOTIFY_UNBOUND_DRIVER
#define BUS_NOTIFY_UNBOUND_DRIVER 0x0005
#endif
/*
* The access to some variables in this macro is racy. We can't use atomic_t
* here because all these variables are exported to debugfs. Some of them even
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment