pci.c 6.63 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2
/*
 * pci.c - Low-Level PCI Access in IA-64
Linus Torvalds's avatar
Linus Torvalds committed
3
 *
Linus Torvalds's avatar
Linus Torvalds committed
4
 * Derived from bios32.c of i386 tree.
5 6 7 8 9
 *
 * Copyright (C) 2002 Hewlett-Packard Co
 *	David Mosberger-Tang <davidm@hpl.hp.com>
 *
 * Note: Above list of copyright holders is incomplete...
Linus Torvalds's avatar
Linus Torvalds committed
10 11 12 13 14 15 16 17
 */
#include <linux/config.h>

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ioport.h>
Linus Torvalds's avatar
Linus Torvalds committed
18
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
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
#include <linux/smp_lock.h>
#include <linux/spinlock.h>

#include <asm/machvec.h>
#include <asm/page.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/io.h>

#include <asm/sal.h>


#ifdef CONFIG_SMP
# include <asm/smp.h>
#endif
#include <asm/irq.h>


#undef DEBUG
#define DEBUG

#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif

Linus Torvalds's avatar
Linus Torvalds committed
46 47 48 49
#ifdef CONFIG_IA64_MCA
extern void ia64_mca_check_errors( void );
#endif

50
struct pci_fixup pcibios_fixups[1];
51 52 53 54

struct pci_ops *pci_root_ops;


Linus Torvalds's avatar
Linus Torvalds committed
55
/*
56 57 58
 * Low-level SAL-based PCI configuration access functions. Note that SAL
 * calls are already serialized (via sal_lock), so we don't need another
 * synchronization mechanism here.  Not using segment number (yet).
Linus Torvalds's avatar
Linus Torvalds committed
59 60
 */

61 62
#define PCI_SAL_ADDRESS(bus, dev, fn, reg) \
	((u64)(bus << 16) | (u64)(dev << 11) | (u64)(fn << 8) | (u64)(reg))
Linus Torvalds's avatar
Linus Torvalds committed
63

64
static int
65
__pci_sal_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value)
66 67 68
{
	int result = 0;
	u64 data = 0;
Linus Torvalds's avatar
Linus Torvalds committed
69

70 71 72 73 74 75 76 77 78
	if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
		return -EINVAL;

	result = ia64_sal_pci_config_read(PCI_SAL_ADDRESS(bus, dev, fn, reg), len, &data);

	*value = (u32) data;

	return result;
}
Linus Torvalds's avatar
Linus Torvalds committed
79

Linus Torvalds's avatar
Linus Torvalds committed
80
static int
81
__pci_sal_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value)
Linus Torvalds's avatar
Linus Torvalds committed
82
{
83 84
	if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
85

86 87 88 89 90
	return ia64_sal_pci_config_write(PCI_SAL_ADDRESS(bus, dev, fn, reg), len, value);
}


static int
91
pci_sal_read (struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
Linus Torvalds's avatar
Linus Torvalds committed
92
{
93 94
	return __pci_sal_read(0, bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn),
			      where, size, value);
Linus Torvalds's avatar
Linus Torvalds committed
95 96
}

Linus Torvalds's avatar
Linus Torvalds committed
97
static int
98
pci_sal_write (struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value)
Linus Torvalds's avatar
Linus Torvalds committed
99
{
100 101
	return __pci_sal_write(0, bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn),
			       where, size, value);
Linus Torvalds's avatar
Linus Torvalds committed
102 103
}

104
struct pci_ops pci_sal_ops = {
105 106
	.read = 	pci_sal_read,
	.write =	pci_sal_write,
Linus Torvalds's avatar
Linus Torvalds committed
107 108
};

109

Linus Torvalds's avatar
Linus Torvalds committed
110 111 112
/*
 * Initialization. Uses the SAL interface
 */
113 114

struct pci_bus *
115
pcibios_scan_root(int bus)
116 117 118 119 120 121 122 123
{
	struct list_head *list = NULL;
	struct pci_bus *pci_bus = NULL;

	list_for_each(list, &pci_root_buses) {
		pci_bus = pci_bus_b(list);
		if (pci_bus->number == bus) {
			/* Already scanned */
124
			printk("PCI: Bus (%02x) already probed\n", bus);
125 126 127 128
			return pci_bus;
		}
	}

129
	printk("PCI: Probing PCI hardware on bus (%02x)\n", bus);
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

	return pci_scan_bus(bus, pci_root_ops, NULL);
}

void __init
pcibios_config_init (void)
{
	if (pci_root_ops)
		return;

	printk("PCI: Using SAL to access configuration space\n");

	pci_root_ops = &pci_sal_ops;

	return;
}

David Mosberger's avatar
David Mosberger committed
147
static int __init
Linus Torvalds's avatar
Linus Torvalds committed
148 149 150
pcibios_init (void)
{
#	define PCI_BUSES_TO_SCAN 255
151
	int i = 0;
Linus Torvalds's avatar
Linus Torvalds committed
152

Linus Torvalds's avatar
Linus Torvalds committed
153 154 155 156
#ifdef CONFIG_IA64_MCA
	ia64_mca_check_errors();    /* For post-failure MCA error logging */
#endif

157 158 159
	pcibios_config_init();

	platform_pci_fixup(0);	/* phase 0 fixups (before buses scanned) */
Linus Torvalds's avatar
Linus Torvalds committed
160 161

	printk("PCI: Probing PCI hardware\n");
Linus Torvalds's avatar
Linus Torvalds committed
162
	for (i = 0; i < PCI_BUSES_TO_SCAN; i++)
163 164 165
		pci_scan_bus(i, pci_root_ops, NULL);

	platform_pci_fixup(1);	/* phase 1 fixups (after buses scanned) */
166
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
167 168
}

169 170
subsys_initcall(pcibios_init);

Linus Torvalds's avatar
Linus Torvalds committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184
/*
 *  Called after each bus is probed, but before its children
 *  are examined.
 */
void __init
pcibios_fixup_bus (struct pci_bus *b)
{
	return;
}

void __init
pcibios_update_resource (struct pci_dev *dev, struct resource *root,
			 struct resource *res, int resource)
{
Linus Torvalds's avatar
Linus Torvalds committed
185 186
	unsigned long where, size;
	u32 reg;
Linus Torvalds's avatar
Linus Torvalds committed
187

Linus Torvalds's avatar
Linus Torvalds committed
188 189 190 191 192
	where = PCI_BASE_ADDRESS_0 + (resource * 4);
	size = res->end - res->start;
	pci_read_config_dword(dev, where, &reg);
	reg = (reg & size) | (((u32)(res->start - root->start)) & ~size);
	pci_write_config_dword(dev, where, reg);
Linus Torvalds's avatar
Linus Torvalds committed
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209

	/* ??? FIXME -- record old value for shutdown.  */
}

void __init
pcibios_update_irq (struct pci_dev *dev, int irq)
{
	pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);

	/* ??? FIXME -- record old value for shutdown.  */
}

void __init
pcibios_fixup_pbus_ranges (struct pci_bus * bus, struct pbus_set_ranges_data * ranges)
{
}

210 211
static inline int
pcibios_enable_resources (struct pci_dev *dev, int mask)
Linus Torvalds's avatar
Linus Torvalds committed
212
{
David Mosberger's avatar
David Mosberger committed
213 214 215 216
	u16 cmd, old_cmd;
	int idx;
	struct resource *r;

217 218 219
	if (!dev)
		return -EINVAL;

David Mosberger's avatar
David Mosberger committed
220 221 222
	pci_read_config_word(dev, PCI_COMMAND, &cmd);
	old_cmd = cmd;
	for (idx=0; idx<6; idx++) {
223 224 225 226
		/* Only set up the desired resources.  */
		if (!(mask & (1 << idx)))
			continue;

David Mosberger's avatar
David Mosberger committed
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
		r = &dev->resource[idx];
		if (!r->start && r->end) {
			printk(KERN_ERR
			       "PCI: Device %s not available because of resource collisions\n",
			       dev->slot_name);
			return -EINVAL;
		}
		if (r->flags & IORESOURCE_IO)
			cmd |= PCI_COMMAND_IO;
		if (r->flags & IORESOURCE_MEM)
			cmd |= PCI_COMMAND_MEMORY;
	}
	if (dev->resource[PCI_ROM_RESOURCE].start)
		cmd |= PCI_COMMAND_MEMORY;
	if (cmd != old_cmd) {
		printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd);
		pci_write_config_word(dev, PCI_COMMAND, cmd);
	}
245 246
	return 0;
}
247

248 249 250 251
int
pcibios_enable_device (struct pci_dev *dev, int mask)
{
	int ret;
David Mosberger's avatar
David Mosberger committed
252

253 254 255 256 257
	ret = pcibios_enable_resources(dev, mask);
	if (ret < 0)
		return ret;

	printk(KERN_INFO "PCI: Found IRQ %d for device %s\n", dev->irq, dev->slot_name);
Linus Torvalds's avatar
Linus Torvalds committed
258 259 260 261
	return 0;
}

void
262 263
pcibios_align_resource (void *data, struct resource *res,
		        unsigned long size, unsigned long align)
Linus Torvalds's avatar
Linus Torvalds committed
264 265 266 267 268 269
{
}

/*
 * PCI BIOS setup, always defaults to SAL interface
 */
Linus Torvalds's avatar
Linus Torvalds committed
270
char * __init
Linus Torvalds's avatar
Linus Torvalds committed
271 272 273 274
pcibios_setup (char *str)
{
	return NULL;
}
Linus Torvalds's avatar
Linus Torvalds committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303

int
pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma,
		     enum pci_mmap_state mmap_state, int write_combine)
{
	/*
	 * I/O space cannot be accessed via normal processor loads and stores on this
	 * platform.
	 */
	if (mmap_state == pci_mmap_io)
		/*
		 * XXX we could relax this for I/O spaces for which ACPI indicates that
		 * the space is 1-to-1 mapped.  But at the moment, we don't support
		 * multiple PCI address spaces and the legacy I/O space is not 1-to-1
		 * mapped, so this is moot.
		 */
		return -EINVAL;

	/*
	 * Leave vm_pgoff as-is, the PCI space address is the physical address on this
	 * platform.
	 */
	vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO);

	if (write_combine)
		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
	else
		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

Linus Torvalds's avatar
Linus Torvalds committed
304
	if (remap_page_range(vma, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT,
305
			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
Linus Torvalds's avatar
Linus Torvalds committed
306 307 308 309
		return -EAGAIN;

	return 0;
}