pci.c 4.06 KB
Newer Older
1 2 3
/* pci.c - Generic PCI interfaces.  */
/*
 *  GRUB  --  GRand Unified Bootloader
4
 *  Copyright (C) 2007,2009  Free Software Foundation, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/dl.h>
#include <grub/pci.h>
22
#include <grub/mm.h>
23
#include <grub/misc.h>
24 25
#include <grub/mm_private.h>
#include <grub/cache.h>
26

27 28
GRUB_MOD_LICENSE ("GPLv3+");

29 30
/* FIXME: correctly support 64-bit architectures.  */
/* #if GRUB_TARGET_SIZEOF_VOID_P == 4 */
31 32 33
struct grub_pci_dma_chunk *
grub_memalign_dma32 (grub_size_t align, grub_size_t size)
{
34 35 36 37 38
  void *ret;
  if (align < 64)
    align = 64;
  size = ALIGN_UP (size, align);
  ret = grub_memalign (align, size);
39 40 41 42 43 44 45 46 47 48
#if GRUB_CPU_SIZEOF_VOID_P == 8
  if ((grub_addr_t) ret >> 32)
    {
      /* Shouldn't happend since the only platform in this case is
	 x86_64-efi and it skips any regions > 4GiB because
	 of EFI bugs anyway.  */
      grub_error (GRUB_ERR_BUG, "allocation outside 32-bit range");
      return 0;
    }
#endif
49 50 51 52
  if (!ret)
    return 0;
  grub_arch_sync_dma_caches (ret, size);
  return ret;
53 54
}

55
/* FIXME: evil.  */
56 57 58
void
grub_dma_free (struct grub_pci_dma_chunk *ch)
{
59 60
  grub_size_t size = (((struct grub_mm_header *) ch) - 1)->size * GRUB_MM_ALIGN;
  grub_arch_sync_dma_caches (ch, size);
61 62
  grub_free (ch);
}
63
/* #endif */
64

65
#ifdef GRUB_MACHINE_MIPS_LOONGSON
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
volatile void *
grub_dma_get_virt (struct grub_pci_dma_chunk *ch)
{
  return (void *) ((((grub_uint32_t) ch) & 0x1fffffff) | 0xa0000000);
}

grub_uint32_t
grub_dma_get_phys (struct grub_pci_dma_chunk *ch)
{
  return (((grub_uint32_t) ch) & 0x1fffffff) | 0x80000000;
}
#else

volatile void *
grub_dma_get_virt (struct grub_pci_dma_chunk *ch)
{
  return (void *) ch;
}

grub_uint32_t
grub_dma_get_phys (struct grub_pci_dma_chunk *ch)
{
88
  return (grub_uint32_t) (grub_addr_t) ch;
89 90 91
}

#endif
92 93

grub_pci_address_t
phcoder's avatar
phcoder committed
94
grub_pci_make_address (grub_pci_device_t dev, int reg)
95
{
phcoder's avatar
phcoder committed
96
  return (1 << 31) | (dev.bus << 16) | (dev.device << 11)
97
    | (dev.function << 8) | reg;
98 99 100
}

void
101
grub_pci_iterate (grub_pci_iteratefunc_t hook, void *hook_data)
102
{
phcoder's avatar
phcoder committed
103
  grub_pci_device_t dev;
104 105
  grub_pci_address_t addr;
  grub_pci_id_t id;
106
  grub_uint32_t hdr;
107

108
  for (dev.bus = 0; dev.bus < GRUB_PCI_NUM_BUS; dev.bus++)
109
    {
110
      for (dev.device = 0; dev.device < GRUB_PCI_NUM_DEVICES; dev.device++)
111
	{
phcoder's avatar
phcoder committed
112
	  for (dev.function = 0; dev.function < 8; dev.function++)
113
	    {
114
	      addr = grub_pci_make_address (dev, GRUB_PCI_REG_PCI_ID);
115 116 117 118
	      id = grub_pci_read (addr);

	      /* Check if there is a device present.  */
	      if (id >> 16 == 0xFFFF)
119 120 121 122 123 124 125 126
		{
		  if (dev.function == 0)
		    /* Devices are required to implement function 0, so if
		       it's missing then there is no device here.  */
		    break;
		  else
		    continue;
		}
127

128
	      if (hook (dev, id, hook_data))
129
		return;
130 131

	      /* Probe only func = 0 if the device if not multifunction */
phcoder's avatar
phcoder committed
132
	      if (dev.function == 0)
133
		{
134
		  addr = grub_pci_make_address (dev, GRUB_PCI_REG_CACHELINE);
135 136 137 138
		  hdr = grub_pci_read (addr);
		  if (!(hdr & 0x800000))
		    break;
		}
139 140 141 142
	    }
	}
    }
}
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

grub_uint8_t
grub_pci_find_capability (grub_pci_device_t dev, grub_uint8_t cap)
{
  grub_uint8_t pos = 0x34;
  int ttl = 48;

  while (ttl--)
    {
      grub_uint8_t id;
      grub_pci_address_t addr;

      addr = grub_pci_make_address (dev, pos);
      pos = grub_pci_read_byte (addr);
      if (pos < 0x40)
	break;

      pos &= ~3;

      addr = grub_pci_make_address (dev, pos);      
      id = grub_pci_read_byte (addr);

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