/*
* 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, see .
*/
#include
#include
#include
#include
#include "ats.h"
bool_t __read_mostly ats_enabled = 0;
boolean_param("ats", ats_enabled);
int enable_ats_device(struct pci_dev *pdev, struct list_head *ats_list)
{
u32 value;
u16 seg = pdev->seg;
u8 bus = pdev->bus, devfn = pdev->devfn;
int pos;
pos = pci_find_ext_capability(seg, bus, devfn, PCI_EXT_CAP_ID_ATS);
BUG_ON(!pos);
if ( iommu_verbose )
dprintk(XENLOG_INFO, "%pp: ATS capability found\n", &pdev->sbdf);
value = pci_conf_read16(pdev->sbdf, pos + ATS_REG_CTL);
if ( value & ATS_ENABLE )
{
struct pci_dev *other;
list_for_each_entry ( other, ats_list, ats.list )
if ( other == pdev )
{
pos = 0;
break;
}
}
if ( !(value & ATS_ENABLE) )
{
value |= ATS_ENABLE;
pci_conf_write16(pdev->sbdf, pos + ATS_REG_CTL, value);
}
if ( pos )
{
pdev->ats.cap_pos = pos;
value = pci_conf_read16(pdev->sbdf, pos + ATS_REG_CAP);
pdev->ats.queue_depth = value & ATS_QUEUE_DEPTH_MASK ?:
ATS_QUEUE_DEPTH_MASK + 1;
list_add(&pdev->ats.list, ats_list);
}
if ( iommu_verbose )
dprintk(XENLOG_INFO, "%pp: ATS %s enabled\n",
&pdev->sbdf, pos ? "is" : "was");
return pos;
}
void disable_ats_device(struct pci_dev *pdev)
{
u32 value;
BUG_ON(!pdev->ats.cap_pos);
value = pci_conf_read16(pdev->sbdf, pdev->ats.cap_pos + ATS_REG_CTL);
value &= ~ATS_ENABLE;
pci_conf_write16(pdev->sbdf, pdev->ats.cap_pos + ATS_REG_CTL, value);
list_del(&pdev->ats.list);
if ( iommu_verbose )
dprintk(XENLOG_INFO, "%pp: ATS is disabled\n", &pdev->sbdf);
}