blob: aba6b958b2a5da25a49adbe76783eca1e5570dc9 [file] [log] [blame]
Daniel Hellstrom26893c12011-05-23 21:04:47 +00001/*
2 * leon_pci.c: LEON Host PCI support
3 *
4 * Copyright (C) 2011 Aeroflex Gaisler AB, Daniel Hellstrom
5 *
6 * Code is partially derived from pcic.c
7 */
8
9#include <linux/of_device.h>
10#include <linux/kernel.h>
11#include <linux/pci.h>
Paul Gortmaker7b64db62011-07-18 15:57:46 -040012#include <linux/export.h>
Daniel Hellstrom26893c12011-05-23 21:04:47 +000013#include <asm/leon.h>
14#include <asm/leon_pci.h>
15
16/* The LEON architecture does not rely on a BIOS or bootloader to setup
17 * PCI for us. The Linux generic routines are used to setup resources,
Bjorn Helgaasac1edcc2012-02-23 20:19:04 -070018 * reset values of configuration-space register settings are preserved.
19 *
20 * PCI Memory and Prefetchable Memory is direct-mapped. However I/O Space is
21 * accessed through a Window which is translated to low 64KB in PCI space, the
22 * first 4KB is not used so 60KB is available.
Daniel Hellstrom26893c12011-05-23 21:04:47 +000023 */
24void leon_pci_init(struct platform_device *ofdev, struct leon_pci_info *info)
25{
Bjorn Helgaas2b591612011-10-28 16:27:58 -060026 LIST_HEAD(resources);
Daniel Hellstrom26893c12011-05-23 21:04:47 +000027 struct pci_bus *root_bus;
28
Bjorn Helgaasac1edcc2012-02-23 20:19:04 -070029 pci_add_resource_offset(&resources, &info->io_space,
30 info->io_space.start - 0x1000);
Bjorn Helgaas2b591612011-10-28 16:27:58 -060031 pci_add_resource(&resources, &info->mem_space);
32
33 root_bus = pci_scan_root_bus(&ofdev->dev, 0, info->ops, info,
34 &resources);
Daniel Hellstrom26893c12011-05-23 21:04:47 +000035 if (root_bus) {
Daniel Hellstrom26893c12011-05-23 21:04:47 +000036 /* Setup IRQs of all devices using custom routines */
37 pci_fixup_irqs(pci_common_swizzle, info->map_irq);
38
39 /* Assign devices with resources */
40 pci_assign_unassigned_resources();
Bjorn Helgaas2b591612011-10-28 16:27:58 -060041 } else {
42 pci_free_resource_list(&resources);
Daniel Hellstrom26893c12011-05-23 21:04:47 +000043 }
44}
45
Daniel Hellstrom26893c12011-05-23 21:04:47 +000046void __devinit pcibios_fixup_bus(struct pci_bus *pbus)
47{
48 struct leon_pci_info *info = pbus->sysdata;
49 struct pci_dev *dev;
50 int i, has_io, has_mem;
51 u16 cmd;
52
Daniel Hellstrom26893c12011-05-23 21:04:47 +000053 list_for_each_entry(dev, &pbus->devices, bus_list) {
54 /*
55 * We can not rely on that the bootloader has enabled I/O
56 * or memory access to PCI devices. Instead we enable it here
57 * if the device has BARs of respective type.
58 */
59 has_io = has_mem = 0;
60 for (i = 0; i < PCI_ROM_RESOURCE; i++) {
61 unsigned long f = dev->resource[i].flags;
62 if (f & IORESOURCE_IO)
63 has_io = 1;
64 else if (f & IORESOURCE_MEM)
65 has_mem = 1;
66 }
67 /* ROM BARs are mapped into 32-bit memory space */
68 if (dev->resource[PCI_ROM_RESOURCE].end != 0) {
69 dev->resource[PCI_ROM_RESOURCE].flags |=
70 IORESOURCE_ROM_ENABLE;
71 has_mem = 1;
72 }
73 pci_bus_read_config_word(pbus, dev->devfn, PCI_COMMAND, &cmd);
74 if (has_io && !(cmd & PCI_COMMAND_IO)) {
75#ifdef CONFIG_PCI_DEBUG
76 printk(KERN_INFO "LEONPCI: Enabling I/O for dev %s\n",
77 pci_name(dev));
78#endif
79 cmd |= PCI_COMMAND_IO;
80 pci_bus_write_config_word(pbus, dev->devfn, PCI_COMMAND,
81 cmd);
82 }
83 if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) {
84#ifdef CONFIG_PCI_DEBUG
85 printk(KERN_INFO "LEONPCI: Enabling MEMORY for dev"
86 "%s\n", pci_name(dev));
87#endif
88 cmd |= PCI_COMMAND_MEMORY;
89 pci_bus_write_config_word(pbus, dev->devfn, PCI_COMMAND,
90 cmd);
91 }
92 }
93}
94
95/*
96 * Other archs parse arguments here.
97 */
98char * __devinit pcibios_setup(char *str)
99{
100 return str;
101}
102
103resource_size_t pcibios_align_resource(void *data, const struct resource *res,
104 resource_size_t size, resource_size_t align)
105{
106 return res->start;
107}
108
109int pcibios_enable_device(struct pci_dev *dev, int mask)
110{
111 return pci_enable_resources(dev, mask);
112}
113
114struct device_node *pci_device_to_OF_node(struct pci_dev *pdev)
115{
116 /*
117 * Currently the OpenBoot nodes are not connected with the PCI device,
118 * this is because the LEON PROM does not create PCI nodes. Eventually
119 * this will change and the same approach as pcic.c can be used to
120 * match PROM nodes with pci devices.
121 */
122 return NULL;
123}
124EXPORT_SYMBOL(pci_device_to_OF_node);
125
126void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
127{
128#ifdef CONFIG_PCI_DEBUG
129 printk(KERN_DEBUG "LEONPCI: Assigning IRQ %02d to %s\n", irq,
130 pci_name(dev));
131#endif
132 pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
133}
134
135/* in/out routines taken from pcic.c
136 *
137 * This probably belongs here rather than ioport.c because
138 * we do not want this crud linked into SBus kernels.
139 * Also, think for a moment about likes of floppy.c that
140 * include architecture specific parts. They may want to redefine ins/outs.
141 *
142 * We do not use horrible macros here because we want to
143 * advance pointer by sizeof(size).
144 */
145void outsb(unsigned long addr, const void *src, unsigned long count)
146{
147 while (count) {
148 count -= 1;
149 outb(*(const char *)src, addr);
150 src += 1;
151 /* addr += 1; */
152 }
153}
154EXPORT_SYMBOL(outsb);
155
156void outsw(unsigned long addr, const void *src, unsigned long count)
157{
158 while (count) {
159 count -= 2;
160 outw(*(const short *)src, addr);
161 src += 2;
162 /* addr += 2; */
163 }
164}
165EXPORT_SYMBOL(outsw);
166
167void outsl(unsigned long addr, const void *src, unsigned long count)
168{
169 while (count) {
170 count -= 4;
171 outl(*(const long *)src, addr);
172 src += 4;
173 /* addr += 4; */
174 }
175}
176EXPORT_SYMBOL(outsl);
177
178void insb(unsigned long addr, void *dst, unsigned long count)
179{
180 while (count) {
181 count -= 1;
182 *(unsigned char *)dst = inb(addr);
183 dst += 1;
184 /* addr += 1; */
185 }
186}
187EXPORT_SYMBOL(insb);
188
189void insw(unsigned long addr, void *dst, unsigned long count)
190{
191 while (count) {
192 count -= 2;
193 *(unsigned short *)dst = inw(addr);
194 dst += 2;
195 /* addr += 2; */
196 }
197}
198EXPORT_SYMBOL(insw);
199
200void insl(unsigned long addr, void *dst, unsigned long count)
201{
202 while (count) {
203 count -= 4;
204 /*
205 * XXX I am sure we are in for an unaligned trap here.
206 */
207 *(unsigned long *)dst = inl(addr);
208 dst += 4;
209 /* addr += 4; */
210 }
211}
212EXPORT_SYMBOL(insl);