aboutsummaryrefslogtreecommitdiffstats
path: root/xen/drivers/pci/pci.c
blob: 25dc5f14866413b911f170d6845c35bda8960838 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/******************************************************************************
 * pci.c
 *
 * Architecture-independent PCI access functions.
 */

#include <xen/init.h>
#include <xen/pci.h>
#include <xen/pci_regs.h>

int pci_find_cap_offset(u16 seg, u8 bus, u8 dev, u8 func, u8 cap)
{
    u8 id;
    int max_cap = 48;
    u8 pos = PCI_CAPABILITY_LIST;
    u16 status;

    status = pci_conf_read16(seg, bus, dev, func, PCI_STATUS);
    if ( (status & PCI_STATUS_CAP_LIST) == 0 )
        return 0;

    while ( max_cap-- )
    {
        pos = pci_conf_read8(seg, bus, dev, func, pos);
        if ( pos < 0x40 )
            break;

        pos &= ~3;
        id = pci_conf_read8(seg, bus, dev, func, pos + PCI_CAP_LIST_ID);

        if ( id == 0xff )
            break;
        else if ( id == cap )
            return pos;

        pos += PCI_CAP_LIST_NEXT;
    }

    return 0;
}

int pci_find_next_cap(u16 seg, u8 bus, unsigned int devfn, u8 pos, int cap)
{
    u8 id;
    int ttl = 48;

    while ( ttl-- )
    {
        pos = pci_conf_read8(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), pos);
        if ( pos < 0x40 )
            break;

        pos &= ~3;
        id = pci_conf_read8(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
                            pos + PCI_CAP_LIST_ID);

        if ( id == 0xff )
            break;
        if ( id == cap )
            return pos;

        pos += PCI_CAP_LIST_NEXT;
    }
    return 0;
}

/**
 * pci_find_ext_capability - Find an extended capability
 * @dev: PCI device to query
 * @cap: capability code
 *
 * Returns the address of the requested extended capability structure
 * within the device's PCI configuration space or 0 if the device does
 * not support it.  Possible values for @cap:
 *
 *  %PCI_EXT_CAP_ID_ERR         Advanced Error Reporting
 *  %PCI_EXT_CAP_ID_VC          Virtual Channel
 *  %PCI_EXT_CAP_ID_DSN         Device Serial Number
 *  %PCI_EXT_CAP_ID_PWR         Power Budgeting
 */
int pci_find_ext_capability(int seg, int bus, int devfn, int cap)
{
    u32 header;
    int ttl = 480; /* 3840 bytes, minimum 8 bytes per capability */
    int pos = 0x100;

    header = pci_conf_read32(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), pos);

    /*
     * If we have no capabilities, this is indicated by cap ID,
     * cap version and next pointer all being 0.
     */
    if ( (header == 0) || (header == -1) )
        return 0;

    while ( ttl-- > 0 ) {
        if ( PCI_EXT_CAP_ID(header) == cap )
            return pos;
        pos = PCI_EXT_CAP_NEXT(header);
        if ( pos < 0x100 )
            break;
        header = pci_conf_read32(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), pos);
    }
    return 0;
}

const char *__init parse_pci(const char *s, unsigned int *seg_p,
                             unsigned int *bus_p, unsigned int *dev_p,
                             unsigned int *func_p)
{
    unsigned long seg = simple_strtoul(s, &s, 16), bus, dev, func;

    if ( *s != ':' )
        return NULL;
    bus = simple_strtoul(s + 1, &s, 16);
    if ( *s == ':' )
        dev = simple_strtoul(s + 1, &s, 16);
    else
    {
        dev = bus;
        bus = seg;
        seg = 0;
    }
    if ( func_p )
    {
        if ( *s != '.' )
            return NULL;
        func = simple_strtoul(s + 1, &s, 0);
    }
    else
        func = 0;
    if ( seg != (seg_p ? (u16)seg : 0) ||
         bus != PCI_BUS(PCI_BDF2(bus, 0)) ||
         dev != PCI_SLOT(PCI_DEVFN(dev, 0)) ||
         func != PCI_FUNC(PCI_DEVFN(0, func)) )
        return NULL;

    if ( seg_p )
        *seg_p = seg;
    *bus_p = bus;
    *dev_p = dev;
    if ( func_p )
        *func_p = func;

    return s;
}