common_bridge.c revision 69bf0a65
14f5e7dd7Smrg/*
24f5e7dd7Smrg * (C) Copyright IBM Corporation 2006
34f5e7dd7Smrg * All Rights Reserved.
44f5e7dd7Smrg *
54f5e7dd7Smrg * Permission is hereby granted, free of charge, to any person obtaining a
64f5e7dd7Smrg * copy of this software and associated documentation files (the "Software"),
74f5e7dd7Smrg * to deal in the Software without restriction, including without limitation
84f5e7dd7Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub
94f5e7dd7Smrg * license, and/or sell copies of the Software, and to permit persons to whom
104f5e7dd7Smrg * the Software is furnished to do so, subject to the following conditions:
114f5e7dd7Smrg *
124f5e7dd7Smrg * The above copyright notice and this permission notice (including the next
134f5e7dd7Smrg * paragraph) shall be included in all copies or substantial portions of the
144f5e7dd7Smrg * Software.
154f5e7dd7Smrg *
164f5e7dd7Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
174f5e7dd7Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
184f5e7dd7Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
194f5e7dd7Smrg * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
204f5e7dd7Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
214f5e7dd7Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
224f5e7dd7Smrg * DEALINGS IN THE SOFTWARE.
234f5e7dd7Smrg */
244f5e7dd7Smrg
254f5e7dd7Smrg/**
264f5e7dd7Smrg * \file common_bridge.c
274f5e7dd7Smrg * Support routines used to process PCI header information for bridges.
284f5e7dd7Smrg *
294f5e7dd7Smrg * \author Ian Romanick <idr@us.ibm.com>
304f5e7dd7Smrg */
314f5e7dd7Smrg
3247fd271eSmrg#if defined(HAVE_CONFIG_H)
334f5e7dd7Smrg#include "config.h"
3447fd271eSmrg#endif
3547fd271eSmrg
364f5e7dd7Smrg#include <stdio.h>
374f5e7dd7Smrg#include <stdlib.h>
384f5e7dd7Smrg#include <ctype.h>
394f5e7dd7Smrg#include <errno.h>
404f5e7dd7Smrg
414f5e7dd7Smrg#if defined(HAVE_STRING_H)
424f5e7dd7Smrg# include <string.h>
434f5e7dd7Smrg#elif defined(HAVE_STRINGS_H)
444f5e7dd7Smrg# include <strings.h>
454f5e7dd7Smrg#endif
464f5e7dd7Smrg
474f5e7dd7Smrg#if defined(HAVE_INTTYPES_H)
484f5e7dd7Smrg# include <inttypes.h>
494f5e7dd7Smrg#elif defined(HAVE_STDINT_H)
504f5e7dd7Smrg# include <stdint.h>
514f5e7dd7Smrg#endif
524f5e7dd7Smrg
534f5e7dd7Smrg#include "pciaccess.h"
544f5e7dd7Smrg#include "pciaccess_private.h"
554f5e7dd7Smrg
564f5e7dd7Smrgstatic int
574f5e7dd7Smrgread_bridge_info( struct pci_device_private * priv )
584f5e7dd7Smrg{
594f5e7dd7Smrg    uint8_t  buf[0x40];
604f5e7dd7Smrg    pciaddr_t bytes;
614f5e7dd7Smrg    int err;
624f5e7dd7Smrg
634f5e7dd7Smrg
644f5e7dd7Smrg    /* Make sure the device has been probed.  If not, header_type won't be
654f5e7dd7Smrg     * set and the rest of this function will fail.
664f5e7dd7Smrg     */
674f5e7dd7Smrg    err = pci_device_probe(& priv->base);
684f5e7dd7Smrg    if (err) {
694f5e7dd7Smrg	return err;
704f5e7dd7Smrg    }
714f5e7dd7Smrg
724f5e7dd7Smrg    switch ( priv->header_type & 0x7f ) {
734f5e7dd7Smrg    case 0x00:
744f5e7dd7Smrg	break;
754f5e7dd7Smrg
764f5e7dd7Smrg    case 0x01: {
774f5e7dd7Smrg	struct pci_bridge_info *info;
784f5e7dd7Smrg
794f5e7dd7Smrg	info = malloc(sizeof(*info));
804f5e7dd7Smrg	if (info != NULL) {
814f5e7dd7Smrg	    pci_device_cfg_read( (struct pci_device *) priv, buf + 0x18, 0x18,
824f5e7dd7Smrg				 0x40 - 0x18, & bytes );
834f5e7dd7Smrg
844f5e7dd7Smrg	    info->primary_bus = buf[0x18];
854f5e7dd7Smrg	    info->secondary_bus = buf[0x19];
864f5e7dd7Smrg	    info->subordinate_bus = buf[0x1a];
874f5e7dd7Smrg	    info->secondary_latency_timer = buf[0x1b];
884f5e7dd7Smrg
894f5e7dd7Smrg	    info->io_type = buf[0x1c] & 0x0f;
904f5e7dd7Smrg	    info->io_base = (((uint32_t) (buf[0x1c] & 0x0f0)) << 8)
914f5e7dd7Smrg	      + (((uint32_t) buf[0x30]) << 16)
924f5e7dd7Smrg	      + (((uint32_t) buf[0x31]) << 24);
934f5e7dd7Smrg
944f5e7dd7Smrg	    info->io_limit = 0x00000fff
954f5e7dd7Smrg	      + (((uint32_t) (buf[0x1d] & 0x0f0)) << 8)
964f5e7dd7Smrg	      + (((uint32_t) buf[0x32]) << 16)
974f5e7dd7Smrg	      + (((uint32_t) buf[0x33]) << 24);
984f5e7dd7Smrg
994f5e7dd7Smrg	    info->mem_type = buf[0x20] & 0x0f;
1004f5e7dd7Smrg	    info->mem_base = (((uint32_t) (buf[0x20] & 0x0f0)) << 16)
1014f5e7dd7Smrg	      + (((uint32_t) buf[0x21]) << 24);
1024f5e7dd7Smrg
1034f5e7dd7Smrg	    info->mem_limit = 0x0000ffff
1044f5e7dd7Smrg	      + (((uint32_t) (buf[0x22] & 0x0f0)) << 16)
1054f5e7dd7Smrg	      + (((uint32_t) buf[0x23]) << 24);
1064f5e7dd7Smrg
1074f5e7dd7Smrg	    info->prefetch_mem_type = buf[0x24] & 0x0f;
1084f5e7dd7Smrg	    info->prefetch_mem_base = (((uint64_t) (buf[0x24] & 0x0f0)) << 16)
1094f5e7dd7Smrg	      + (((uint64_t) buf[0x25]) << 24)
1104f5e7dd7Smrg	      + (((uint64_t) buf[0x28]) << 32)
1114f5e7dd7Smrg	      + (((uint64_t) buf[0x29]) << 40)
1124f5e7dd7Smrg	      + (((uint64_t) buf[0x2a]) << 48)
1134f5e7dd7Smrg	      + (((uint64_t) buf[0x2b]) << 56);
1144f5e7dd7Smrg
1154f5e7dd7Smrg	    info->prefetch_mem_limit = 0x0000ffff
1164f5e7dd7Smrg	      + (((uint64_t) (buf[0x26] & 0x0f0)) << 16)
1174f5e7dd7Smrg	      + (((uint64_t) buf[0x27]) << 24)
1184f5e7dd7Smrg	      + (((uint64_t) buf[0x2c]) << 32)
1194f5e7dd7Smrg	      + (((uint64_t) buf[0x2d]) << 40)
1204f5e7dd7Smrg	      + (((uint64_t) buf[0x2e]) << 48)
1214f5e7dd7Smrg	      + (((uint64_t) buf[0x2f]) << 56);
1224f5e7dd7Smrg
1234f5e7dd7Smrg	    info->bridge_control = ((uint16_t) buf[0x3e])
1244f5e7dd7Smrg	      + (((uint16_t) buf[0x3f]) << 8);
1254f5e7dd7Smrg
1264f5e7dd7Smrg	    info->secondary_status = ((uint16_t) buf[0x1e])
1274f5e7dd7Smrg	      + (((uint16_t) buf[0x1f]) << 8);
1284f5e7dd7Smrg	}
1294f5e7dd7Smrg
1304f5e7dd7Smrg	priv->bridge.pci = info;
1314f5e7dd7Smrg	break;
1324f5e7dd7Smrg    }
1334f5e7dd7Smrg
1344f5e7dd7Smrg    case 0x02: {
1354f5e7dd7Smrg	struct pci_pcmcia_bridge_info *info;
1364f5e7dd7Smrg
1374f5e7dd7Smrg	info = malloc(sizeof(*info));
1384f5e7dd7Smrg	if (info != NULL) {
1394f5e7dd7Smrg	    pci_device_cfg_read( (struct pci_device *) priv, buf + 0x16, 0x16,
1404f5e7dd7Smrg				 0x40 - 0x16, & bytes );
1414f5e7dd7Smrg
1424f5e7dd7Smrg	    info->primary_bus = buf[0x18];
1434f5e7dd7Smrg	    info->card_bus = buf[0x19];
1444f5e7dd7Smrg	    info->subordinate_bus = buf[0x1a];
1454f5e7dd7Smrg	    info->cardbus_latency_timer = buf[0x1b];
1464f5e7dd7Smrg
1474f5e7dd7Smrg	    info->mem[0].base = (((uint32_t) buf[0x1c]))
1484f5e7dd7Smrg	      + (((uint32_t) buf[0x1d]) << 8)
1494f5e7dd7Smrg	      + (((uint32_t) buf[0x1e]) << 16)
1504f5e7dd7Smrg	      + (((uint32_t) buf[0x1f]) << 24);
1514f5e7dd7Smrg
1524f5e7dd7Smrg	    info->mem[0].limit = (((uint32_t) buf[0x20]))
1534f5e7dd7Smrg	      + (((uint32_t) buf[0x21]) << 8)
1544f5e7dd7Smrg	      + (((uint32_t) buf[0x22]) << 16)
1554f5e7dd7Smrg	      + (((uint32_t) buf[0x23]) << 24);
1564f5e7dd7Smrg
1574f5e7dd7Smrg	    info->mem[1].base = (((uint32_t) buf[0x24]))
1584f5e7dd7Smrg	      + (((uint32_t) buf[0x25]) << 8)
1594f5e7dd7Smrg	      + (((uint32_t) buf[0x26]) << 16)
1604f5e7dd7Smrg	      + (((uint32_t) buf[0x27]) << 24);
1614f5e7dd7Smrg
1624f5e7dd7Smrg	    info->mem[1].limit = (((uint32_t) buf[0x28]))
1634f5e7dd7Smrg	      + (((uint32_t) buf[0x29]) << 8)
1644f5e7dd7Smrg	      + (((uint32_t) buf[0x2a]) << 16)
1654f5e7dd7Smrg	      + (((uint32_t) buf[0x2b]) << 24);
1664f5e7dd7Smrg
1674f5e7dd7Smrg	    info->io[0].base = (((uint32_t) buf[0x2c]))
1684f5e7dd7Smrg	      + (((uint32_t) buf[0x2d]) << 8)
1694f5e7dd7Smrg	      + (((uint32_t) buf[0x2e]) << 16)
1704f5e7dd7Smrg	      + (((uint32_t) buf[0x2f]) << 24);
1714f5e7dd7Smrg
1724f5e7dd7Smrg	    info->io[0].limit = (((uint32_t) buf[0x30]))
1734f5e7dd7Smrg	      + (((uint32_t) buf[0x31]) << 8)
1744f5e7dd7Smrg	      + (((uint32_t) buf[0x32]) << 16)
1754f5e7dd7Smrg	      + (((uint32_t) buf[0x33]) << 24);
1764f5e7dd7Smrg
1774f5e7dd7Smrg	    info->io[1].base = (((uint32_t) buf[0x34]))
1784f5e7dd7Smrg	      + (((uint32_t) buf[0x35]) << 8)
1794f5e7dd7Smrg	      + (((uint32_t) buf[0x36]) << 16)
1804f5e7dd7Smrg	      + (((uint32_t) buf[0x37]) << 24);
1814f5e7dd7Smrg
1824f5e7dd7Smrg	    info->io[1].limit = (((uint32_t) buf[0x38]))
1834f5e7dd7Smrg	      + (((uint32_t) buf[0x39]) << 8)
1844f5e7dd7Smrg	      + (((uint32_t) buf[0x3a]) << 16)
1854f5e7dd7Smrg	      + (((uint32_t) buf[0x3b]) << 24);
1864f5e7dd7Smrg
1874f5e7dd7Smrg	    info->secondary_status = ((uint16_t) buf[0x16])
1884f5e7dd7Smrg	      + (((uint16_t) buf[0x17]) << 8);
1894f5e7dd7Smrg
1904f5e7dd7Smrg	    info->bridge_control = ((uint16_t) buf[0x3e])
1914f5e7dd7Smrg	      + (((uint16_t) buf[0x3f]) << 8);
1924f5e7dd7Smrg	}
1934f5e7dd7Smrg
1944f5e7dd7Smrg	priv->bridge.pcmcia = info;
1954f5e7dd7Smrg	break;
1964f5e7dd7Smrg    }
1974f5e7dd7Smrg    }
1984f5e7dd7Smrg
1994f5e7dd7Smrg    return 0;
2004f5e7dd7Smrg}
2014f5e7dd7Smrg
2024f5e7dd7Smrg
2034f5e7dd7Smrg/**
2044f5e7dd7Smrg * Get the PCI bridge information for a device
2054f5e7dd7Smrg *
2064f5e7dd7Smrg * \returns
2074f5e7dd7Smrg * If \c dev is a PCI-to-PCI bridge, a pointer to a \c pci_bridge_info
2084f5e7dd7Smrg * structure.  Otherwise, \c NULL is returned.
2094f5e7dd7Smrg */
2104f5e7dd7Smrgconst struct pci_bridge_info *
2114f5e7dd7Smrgpci_device_get_bridge_info( struct pci_device * dev )
2124f5e7dd7Smrg{
2134f5e7dd7Smrg    struct pci_device_private * priv = (struct pci_device_private *) dev;
2144f5e7dd7Smrg
2154f5e7dd7Smrg    if (priv->bridge.pci == NULL) {
2164f5e7dd7Smrg	read_bridge_info(priv);
2174f5e7dd7Smrg    }
2184f5e7dd7Smrg
2194f5e7dd7Smrg    return (priv->header_type == 1) ? priv->bridge.pci : NULL;
2204f5e7dd7Smrg}
2214f5e7dd7Smrg
2224f5e7dd7Smrg
2234f5e7dd7Smrg/**
2244f5e7dd7Smrg * Get the PCMCIA bridge information for a device
2254f5e7dd7Smrg *
2264f5e7dd7Smrg * \returns
2274f5e7dd7Smrg * If \c dev is a PCI-to-PCMCIA bridge, a pointer to a
2284f5e7dd7Smrg * \c pci_pcmcia_bridge_info structure.  Otherwise, \c NULL is returned.
2294f5e7dd7Smrg */
2304f5e7dd7Smrgconst struct pci_pcmcia_bridge_info *
2314f5e7dd7Smrgpci_device_get_pcmcia_bridge_info( struct pci_device * dev )
2324f5e7dd7Smrg{
2334f5e7dd7Smrg    struct pci_device_private * priv = (struct pci_device_private *) dev;
2344f5e7dd7Smrg
2354f5e7dd7Smrg    if (priv->bridge.pcmcia == NULL) {
2364f5e7dd7Smrg	read_bridge_info(priv);
2374f5e7dd7Smrg    }
2384f5e7dd7Smrg
2394f5e7dd7Smrg    return (priv->header_type == 2) ? priv->bridge.pcmcia : NULL;
2404f5e7dd7Smrg}
2414f5e7dd7Smrg
2424f5e7dd7Smrg
2434f5e7dd7Smrg/**
2444f5e7dd7Smrg * Determine the primary, secondary, and subordinate buses for a bridge
2454f5e7dd7Smrg *
2464f5e7dd7Smrg * Determines the IDs of the primary, secondary, and subordinate buses for
2474f5e7dd7Smrg * a specified bridge.  Not all bridges directly store this information
2484f5e7dd7Smrg * (e.g., PCI-to-ISA bridges).  For those bridges, no error is returned, but
2494f5e7dd7Smrg * -1 is stored in the bus IDs that don't make sense.
2504f5e7dd7Smrg *
2514f5e7dd7Smrg * For example, for a PCI-to-ISA bridge, \c primary_bus will be set to the ID
2524f5e7dd7Smrg * of the bus containing the device and both \c secondary_bus and
2534f5e7dd7Smrg * \c subordinate_bus will be set to -1.
2544f5e7dd7Smrg *
2554f5e7dd7Smrg * \return
2564f5e7dd7Smrg * On success, zero is returned.  If \c dev is not a bridge, \c ENODEV is
2574f5e7dd7Smrg * returned.
2584f5e7dd7Smrg *
2594f5e7dd7Smrg * \bug
2604f5e7dd7Smrg * Host bridges are handled the same way as PCI-to-ISA bridges.  This is
2614f5e7dd7Smrg * almost certainly not correct.
2624f5e7dd7Smrg */
2634f5e7dd7Smrgint
2644f5e7dd7Smrgpci_device_get_bridge_buses(struct pci_device * dev, int *primary_bus,
2654f5e7dd7Smrg			    int *secondary_bus, int *subordinate_bus)
2664f5e7dd7Smrg{
2674f5e7dd7Smrg    struct pci_device_private * priv = (struct pci_device_private *) dev;
2684f5e7dd7Smrg
2694f5e7dd7Smrg    /* If the device isn't a bridge, return an error.
2704f5e7dd7Smrg     */
2714f5e7dd7Smrg
2724f5e7dd7Smrg    if (((dev->device_class >> 16) & 0x0ff) != 0x06) {
2734f5e7dd7Smrg	return ENODEV;
2744f5e7dd7Smrg    }
2754f5e7dd7Smrg
27669bf0a65Smrg    if (!priv->bridge.pci) {
27769bf0a65Smrg	return ENODEV;
27869bf0a65Smrg    }
27969bf0a65Smrg
2804f5e7dd7Smrg    switch ((dev->device_class >> 8) & 0x0ff) {
2814f5e7dd7Smrg    case 0x00:
2824f5e7dd7Smrg	/* What to do for host bridges?  I'm pretty sure this isn't right.
2834f5e7dd7Smrg	 */
2844f5e7dd7Smrg	*primary_bus = dev->bus;
2854f5e7dd7Smrg	*secondary_bus = -1;
2864f5e7dd7Smrg	*subordinate_bus = -1;
2874f5e7dd7Smrg	break;
2884f5e7dd7Smrg
2894f5e7dd7Smrg    case 0x01:
2904f5e7dd7Smrg    case 0x02:
2914f5e7dd7Smrg    case 0x03:
2924f5e7dd7Smrg	*primary_bus = dev->bus;
2934f5e7dd7Smrg	*secondary_bus = -1;
2944f5e7dd7Smrg	*subordinate_bus = -1;
2954f5e7dd7Smrg	break;
2964f5e7dd7Smrg
2974f5e7dd7Smrg    case 0x04:
2984f5e7dd7Smrg    if (priv->bridge.pci == NULL)
2994f5e7dd7Smrg        read_bridge_info(priv);
3004f5e7dd7Smrg    if (priv->header_type == 0x01) {
3014f5e7dd7Smrg	*primary_bus = priv->bridge.pci->primary_bus;
3024f5e7dd7Smrg	*secondary_bus = priv->bridge.pci->secondary_bus;
3034f5e7dd7Smrg	*subordinate_bus = priv->bridge.pci->subordinate_bus;
3044f5e7dd7Smrg    } else {
3054f5e7dd7Smrg	*primary_bus = dev->bus;
3064f5e7dd7Smrg	*secondary_bus = -1;
3074f5e7dd7Smrg	*subordinate_bus = -1;
3084f5e7dd7Smrg    }
3094f5e7dd7Smrg	break;
3104f5e7dd7Smrg
3114f5e7dd7Smrg    case 0x07:
3124f5e7dd7Smrg    if (priv->bridge.pcmcia == NULL)
3134f5e7dd7Smrg        read_bridge_info(priv);
3144f5e7dd7Smrg    if (priv->header_type == 0x02) {
3154f5e7dd7Smrg	*primary_bus = priv->bridge.pcmcia->primary_bus;
3164f5e7dd7Smrg	*secondary_bus = priv->bridge.pcmcia->card_bus;
3174f5e7dd7Smrg	*subordinate_bus = priv->bridge.pcmcia->subordinate_bus;
3184f5e7dd7Smrg    } else {
3194f5e7dd7Smrg	*primary_bus = dev->bus;
3204f5e7dd7Smrg	*secondary_bus = -1;
3214f5e7dd7Smrg	*subordinate_bus = -1;
3224f5e7dd7Smrg    }
3234f5e7dd7Smrg	break;
3244f5e7dd7Smrg    }
3254f5e7dd7Smrg
3264f5e7dd7Smrg    return 0;
3274f5e7dd7Smrg}
328