1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms and conditions of the GNU General Public License,
4 * version 2, as published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope it will be useful, but WITHOUT
7 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
8 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
9 * more details.
10 *
11 * You should have received a copy of the GNU General Public License along with
12 * this program; If not, see <http://www.gnu.org/licenses/>.
13 */
14
15 #include <xen/sched.h>
16 #include <xen/pci.h>
17 #include <xen/pci_regs.h>
18 #include "../ats.h"
19
20 bool_t __read_mostly ats_enabled = 0;
21 boolean_param("ats", ats_enabled);
22
enable_ats_device(struct pci_dev * pdev,struct list_head * ats_list)23 int enable_ats_device(struct pci_dev *pdev, struct list_head *ats_list)
24 {
25 u32 value;
26 u16 seg = pdev->seg;
27 u8 bus = pdev->bus, devfn = pdev->devfn;
28 int pos;
29
30 pos = pci_find_ext_capability(seg, bus, devfn, PCI_EXT_CAP_ID_ATS);
31 BUG_ON(!pos);
32
33 if ( iommu_verbose )
34 dprintk(XENLOG_INFO, "%04x:%02x:%02x.%u: ATS capability found\n",
35 seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
36
37 value = pci_conf_read16(seg, bus, PCI_SLOT(devfn),
38 PCI_FUNC(devfn), pos + ATS_REG_CTL);
39 if ( value & ATS_ENABLE )
40 {
41 struct pci_dev *other;
42
43 list_for_each_entry ( other, ats_list, ats.list )
44 if ( other == pdev )
45 {
46 pos = 0;
47 break;
48 }
49 }
50
51 if ( !(value & ATS_ENABLE) )
52 {
53 value |= ATS_ENABLE;
54 pci_conf_write16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
55 pos + ATS_REG_CTL, value);
56 }
57
58 if ( pos )
59 {
60 pdev->ats.cap_pos = pos;
61 value = pci_conf_read16(seg, bus, PCI_SLOT(devfn),
62 PCI_FUNC(devfn), pos + ATS_REG_CAP);
63 pdev->ats.queue_depth = value & ATS_QUEUE_DEPTH_MASK ?:
64 ATS_QUEUE_DEPTH_MASK + 1;
65 list_add(&pdev->ats.list, ats_list);
66 }
67
68 if ( iommu_verbose )
69 dprintk(XENLOG_INFO, "%04x:%02x:%02x.%u: ATS %s enabled\n",
70 seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
71 pos ? "is" : "was");
72
73 return pos;
74 }
75
disable_ats_device(struct pci_dev * pdev)76 void disable_ats_device(struct pci_dev *pdev)
77 {
78 u32 value;
79 u16 seg = pdev->seg;
80 u8 bus = pdev->bus, devfn = pdev->devfn;
81
82 BUG_ON(!pdev->ats.cap_pos);
83
84 value = pci_conf_read16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
85 pdev->ats.cap_pos + ATS_REG_CTL);
86 value &= ~ATS_ENABLE;
87 pci_conf_write16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
88 pdev->ats.cap_pos + ATS_REG_CTL, value);
89
90 list_del(&pdev->ats.list);
91
92 if ( iommu_verbose )
93 dprintk(XENLOG_INFO, "%04x:%02x:%02x.%u: ATS is disabled\n",
94 seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
95 }
96