common_capability.c revision 28d65773
1/*
2 * (C) Copyright IBM Corporation 2006
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * on the rights to use, copy, modify, merge, publish, distribute, sub
9 * license, and/or sell copies of the Software, and to permit persons to whom
10 * the Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
19 * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25/**
26 * \file common_capability.c
27 * Platform independent PCI capability related routines.
28 *
29 * In addition to including the interface glue for \c pci_device_get_agp_info,
30 * this file also contains a generic implementation of that function.
31 *
32 * \author Ian Romanick <idr@us.ibm.com>
33 */
34
35#include <stdlib.h>
36#include <stdio.h>
37#include <errno.h>
38
39#include "pciaccess.h"
40#include "pciaccess_private.h"
41
42/**
43 * Generic implementation of \c pci_system_methods::fill_capabilities.
44 *
45 * \param dev   Device whose capability information is to be processed.
46 *
47 * \return
48 * Zero on success or an errno value on failure.
49 *
50 * \todo
51 * Once more than just the AGP capability is supported, the body of each of
52 * the cases in the capability processing loop should probably be broken out
53 * into its own function.
54 *
55 * \todo
56 * Once more than just the AGP capability is supported, some care will need
57 * to be taken in partial failure cases.  If, say, the first capability is
58 * correctly processed but the second fails, the function would be re-called
59 * later to try again for the second capability.  This could lead to memory
60 * leaks or other quirky behavior.
61 */
62_pci_hidden int
63pci_fill_capabilities_generic( struct pci_device * dev )
64{
65    struct pci_device_private * const dev_priv =
66      (struct pci_device_private *) dev;
67    int       err;
68    uint16_t  status;
69    uint8_t   cap_offset;
70
71
72    err = pci_device_cfg_read_u16( dev, & status, 6 );
73    if ( err ) {
74	return err;
75    }
76
77    /* Are PCI capabilities supported by this device?
78     */
79    if ( (status & 0x0010) == 0 ) {
80	return ENOSYS;
81    }
82
83    err = pci_device_cfg_read_u8( dev, & cap_offset, 52 );
84    if ( err ) {
85	return err;
86    }
87
88
89    /* Process each of the capabilities list in the PCI header.
90     */
91    while ( cap_offset != 0 ) {
92	uint8_t cap_id;
93	uint8_t next_cap;
94
95	err = pci_device_cfg_read_u8( dev, & cap_id, cap_offset );
96	if ( err ) {
97	    return err;
98	}
99
100	err = pci_device_cfg_read_u8( dev, & next_cap, cap_offset + 1 );
101	if ( err ) {
102	    return err;
103	}
104
105	switch ( cap_id ) {
106	case 2: {
107	    struct pci_agp_info * agp_info;
108	    uint32_t agp_status;
109	    uint8_t agp_ver;
110
111
112	    err = pci_device_cfg_read_u8( dev, & agp_ver, cap_offset + 2 );
113	    if ( err ) {
114		return err;
115	    }
116
117	    err = pci_device_cfg_read_u32( dev, & agp_status, cap_offset + 4 );
118	    if ( err ) {
119		return err;
120	    }
121
122	    agp_info = calloc( 1, sizeof( struct pci_agp_info ) );
123	    if ( agp_info == NULL ) {
124		return ENOMEM;
125	    }
126
127	    agp_info->config_offset = cap_offset;
128
129	    agp_info->major_version = (agp_ver & 0x0f0) >> 4;
130	    agp_info->minor_version = (agp_ver & 0x00f);
131
132	    agp_info->rates = (agp_status & 0x07);
133
134	    /* If AGP3 is supported, then the meaning of the rates values
135	     * changes.
136	     */
137	    if ( (agp_status & 0x08) != 0 ) {
138		agp_info->rates <<= 2;
139	    }
140
141	    /* Some devices, notably motherboard chipsets, have the AGP3
142	     * capability set and the 4x bit set.  This results in an
143	     * impossible 16x mode being listed as available.  I'm not 100%
144	     * sure this is the right solution.
145	     */
146	    agp_info->rates &= 0x0f;
147
148
149	    agp_info->fast_writes = (agp_status & 0x0010) != 0;
150	    agp_info->addr64 =      (agp_status & 0x0020) != 0;
151	    agp_info->htrans =      (agp_status & 0x0040) == 0;
152	    agp_info->gart64 =      (agp_status & 0x0080) != 0;
153	    agp_info->coherent =    (agp_status & 0x0100) != 0;
154	    agp_info->sideband =    (agp_status & 0x0200) != 0;
155	    agp_info->isochronus =  (agp_status & 0x10000) != 0;
156
157	    agp_info->async_req_size = 4 + (1 << ((agp_status & 0xe000) >> 13));
158	    agp_info->calibration_cycle_timing = ((agp_status & 0x1c00) >> 10);
159	    agp_info->max_requests = 1 + ((agp_status & 0xff000000) >> 24);
160
161	    dev_priv->agp = agp_info;
162	    break;
163	}
164
165	/* No other capabilities are currently handled.
166	 */
167	default:
168	    printf( "Unknown cap 0x%02x @ 0x%02x\n", cap_id, cap_offset );
169	    break;
170	}
171
172	cap_offset = next_cap;
173    }
174
175    return 0;
176}
177
178
179/**
180 * Get AGP capability data for a device.
181 */
182const struct pci_agp_info *
183pci_device_get_agp_info( struct pci_device * dev )
184{
185    struct pci_device_private * dev_priv = (struct pci_device_private *) dev;
186
187    if ( dev == NULL ) {
188	return NULL;
189    }
190
191    if ( dev_priv->agp == NULL ) {
192	(void) (*pci_sys->methods->fill_capabilities)( dev );
193    }
194
195    return dev_priv->agp;
196}
197