1aab6724cSSimon Glass /*
2aab6724cSSimon Glass * Copyright (c) 2014 Google, Inc
3aab6724cSSimon Glass *
4aab6724cSSimon Glass * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
5aab6724cSSimon Glass * Andreas Heppel <aheppel@sysgo.de>
6aab6724cSSimon Glass *
7aab6724cSSimon Glass * (C) Copyright 2002, 2003
8aab6724cSSimon Glass * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
9aab6724cSSimon Glass *
10aab6724cSSimon Glass * SPDX-License-Identifier: GPL-2.0+
11aab6724cSSimon Glass */
12aab6724cSSimon Glass
13aab6724cSSimon Glass #include <common.h>
14aec241dfSSimon Glass #include <dm.h>
15aab6724cSSimon Glass #include <errno.h>
16aab6724cSSimon Glass #include <pci.h>
17aab6724cSSimon Glass #include <asm/io.h>
18aab6724cSSimon Glass
pci_class_str(u8 class)19aab6724cSSimon Glass const char *pci_class_str(u8 class)
20aab6724cSSimon Glass {
21aab6724cSSimon Glass switch (class) {
22aab6724cSSimon Glass case PCI_CLASS_NOT_DEFINED:
23aab6724cSSimon Glass return "Build before PCI Rev2.0";
24aab6724cSSimon Glass break;
25aab6724cSSimon Glass case PCI_BASE_CLASS_STORAGE:
26aab6724cSSimon Glass return "Mass storage controller";
27aab6724cSSimon Glass break;
28aab6724cSSimon Glass case PCI_BASE_CLASS_NETWORK:
29aab6724cSSimon Glass return "Network controller";
30aab6724cSSimon Glass break;
31aab6724cSSimon Glass case PCI_BASE_CLASS_DISPLAY:
32aab6724cSSimon Glass return "Display controller";
33aab6724cSSimon Glass break;
34aab6724cSSimon Glass case PCI_BASE_CLASS_MULTIMEDIA:
35aab6724cSSimon Glass return "Multimedia device";
36aab6724cSSimon Glass break;
37aab6724cSSimon Glass case PCI_BASE_CLASS_MEMORY:
38aab6724cSSimon Glass return "Memory controller";
39aab6724cSSimon Glass break;
40aab6724cSSimon Glass case PCI_BASE_CLASS_BRIDGE:
41aab6724cSSimon Glass return "Bridge device";
42aab6724cSSimon Glass break;
43aab6724cSSimon Glass case PCI_BASE_CLASS_COMMUNICATION:
44aab6724cSSimon Glass return "Simple comm. controller";
45aab6724cSSimon Glass break;
46aab6724cSSimon Glass case PCI_BASE_CLASS_SYSTEM:
47aab6724cSSimon Glass return "Base system peripheral";
48aab6724cSSimon Glass break;
49aab6724cSSimon Glass case PCI_BASE_CLASS_INPUT:
50aab6724cSSimon Glass return "Input device";
51aab6724cSSimon Glass break;
52aab6724cSSimon Glass case PCI_BASE_CLASS_DOCKING:
53aab6724cSSimon Glass return "Docking station";
54aab6724cSSimon Glass break;
55aab6724cSSimon Glass case PCI_BASE_CLASS_PROCESSOR:
56aab6724cSSimon Glass return "Processor";
57aab6724cSSimon Glass break;
58aab6724cSSimon Glass case PCI_BASE_CLASS_SERIAL:
59aab6724cSSimon Glass return "Serial bus controller";
60aab6724cSSimon Glass break;
61aab6724cSSimon Glass case PCI_BASE_CLASS_INTELLIGENT:
62aab6724cSSimon Glass return "Intelligent controller";
63aab6724cSSimon Glass break;
64aab6724cSSimon Glass case PCI_BASE_CLASS_SATELLITE:
65aab6724cSSimon Glass return "Satellite controller";
66aab6724cSSimon Glass break;
67aab6724cSSimon Glass case PCI_BASE_CLASS_CRYPT:
68aab6724cSSimon Glass return "Cryptographic device";
69aab6724cSSimon Glass break;
70aab6724cSSimon Glass case PCI_BASE_CLASS_SIGNAL_PROCESSING:
71aab6724cSSimon Glass return "DSP";
72aab6724cSSimon Glass break;
73aab6724cSSimon Glass case PCI_CLASS_OTHERS:
74aab6724cSSimon Glass return "Does not fit any class";
75aab6724cSSimon Glass break;
76aab6724cSSimon Glass default:
77aab6724cSSimon Glass return "???";
78aab6724cSSimon Glass break;
79aab6724cSSimon Glass };
80aab6724cSSimon Glass }
81aab6724cSSimon Glass
pci_skip_dev(struct pci_controller * hose,pci_dev_t dev)82aab6724cSSimon Glass __weak int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev)
83aab6724cSSimon Glass {
84aab6724cSSimon Glass /*
85aab6724cSSimon Glass * Check if pci device should be skipped in configuration
86aab6724cSSimon Glass */
87aab6724cSSimon Glass if (dev == PCI_BDF(hose->first_busno, 0, 0)) {
88aab6724cSSimon Glass #if defined(CONFIG_PCI_CONFIG_HOST_BRIDGE) /* don't skip host bridge */
89aab6724cSSimon Glass /*
90aab6724cSSimon Glass * Only skip configuration if "pciconfighost" is not set
91aab6724cSSimon Glass */
92*00caae6dSSimon Glass if (env_get("pciconfighost") == NULL)
93aab6724cSSimon Glass return 1;
94aab6724cSSimon Glass #else
95aab6724cSSimon Glass return 1;
96aab6724cSSimon Glass #endif
97aab6724cSSimon Glass }
98aab6724cSSimon Glass
99aab6724cSSimon Glass return 0;
100aab6724cSSimon Glass }
101aab6724cSSimon Glass
1027e78b9efSSimon Glass #if !defined(CONFIG_DM_PCI) || defined(CONFIG_DM_PCI_COMPAT)
103aab6724cSSimon Glass /* Get a virtual address associated with a BAR region */
pci_map_bar(pci_dev_t pdev,int bar,int flags)104aab6724cSSimon Glass void *pci_map_bar(pci_dev_t pdev, int bar, int flags)
105aab6724cSSimon Glass {
106aab6724cSSimon Glass pci_addr_t pci_bus_addr;
107aab6724cSSimon Glass u32 bar_response;
108aab6724cSSimon Glass
109aab6724cSSimon Glass /* read BAR address */
110aab6724cSSimon Glass pci_read_config_dword(pdev, bar, &bar_response);
111aab6724cSSimon Glass pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
112aab6724cSSimon Glass
113aab6724cSSimon Glass /*
114aab6724cSSimon Glass * Pass "0" as the length argument to pci_bus_to_virt. The arg
115aab6724cSSimon Glass * isn't actualy used on any platform because u-boot assumes a static
116aab6724cSSimon Glass * linear mapping. In the future, this could read the BAR size
117aab6724cSSimon Glass * and pass that as the size if needed.
118aab6724cSSimon Glass */
119aab6724cSSimon Glass return pci_bus_to_virt(pdev, pci_bus_addr, flags, 0, MAP_NOCACHE);
120aab6724cSSimon Glass }
121aab6724cSSimon Glass
pci_write_bar32(struct pci_controller * hose,pci_dev_t dev,int barnum,u32 addr_and_ctrl)122aab6724cSSimon Glass void pci_write_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum,
123aab6724cSSimon Glass u32 addr_and_ctrl)
124aab6724cSSimon Glass {
125aab6724cSSimon Glass int bar;
126aab6724cSSimon Glass
127aab6724cSSimon Glass bar = PCI_BASE_ADDRESS_0 + barnum * 4;
128aab6724cSSimon Glass pci_hose_write_config_dword(hose, dev, bar, addr_and_ctrl);
129aab6724cSSimon Glass }
130aab6724cSSimon Glass
pci_read_bar32(struct pci_controller * hose,pci_dev_t dev,int barnum)131aab6724cSSimon Glass u32 pci_read_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum)
132aab6724cSSimon Glass {
133aab6724cSSimon Glass u32 addr;
134aab6724cSSimon Glass int bar;
135aab6724cSSimon Glass
136aab6724cSSimon Glass bar = PCI_BASE_ADDRESS_0 + barnum * 4;
137aab6724cSSimon Glass pci_hose_read_config_dword(hose, dev, bar, &addr);
138aab6724cSSimon Glass if (addr & PCI_BASE_ADDRESS_SPACE_IO)
139aab6724cSSimon Glass return addr & PCI_BASE_ADDRESS_IO_MASK;
140aab6724cSSimon Glass else
141aab6724cSSimon Glass return addr & PCI_BASE_ADDRESS_MEM_MASK;
142aab6724cSSimon Glass }
143aab6724cSSimon Glass
__pci_hose_bus_to_phys(struct pci_controller * hose,pci_addr_t bus_addr,unsigned long flags,unsigned long skip_mask,phys_addr_t * pa)144aab6724cSSimon Glass int __pci_hose_bus_to_phys(struct pci_controller *hose,
145aab6724cSSimon Glass pci_addr_t bus_addr,
146aab6724cSSimon Glass unsigned long flags,
147aab6724cSSimon Glass unsigned long skip_mask,
148aab6724cSSimon Glass phys_addr_t *pa)
149aab6724cSSimon Glass {
150aab6724cSSimon Glass struct pci_region *res;
151aab6724cSSimon Glass int i;
152aab6724cSSimon Glass
153aab6724cSSimon Glass for (i = 0; i < hose->region_count; i++) {
154aab6724cSSimon Glass res = &hose->regions[i];
155aab6724cSSimon Glass
156aab6724cSSimon Glass if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
157aab6724cSSimon Glass continue;
158aab6724cSSimon Glass
159aab6724cSSimon Glass if (res->flags & skip_mask)
160aab6724cSSimon Glass continue;
161aab6724cSSimon Glass
162aab6724cSSimon Glass if (bus_addr >= res->bus_start &&
163aab6724cSSimon Glass (bus_addr - res->bus_start) < res->size) {
164aab6724cSSimon Glass *pa = (bus_addr - res->bus_start + res->phys_start);
165aab6724cSSimon Glass return 0;
166aab6724cSSimon Glass }
167aab6724cSSimon Glass }
168aab6724cSSimon Glass
169aab6724cSSimon Glass return 1;
170aab6724cSSimon Glass }
171aab6724cSSimon Glass
pci_hose_bus_to_phys(struct pci_controller * hose,pci_addr_t bus_addr,unsigned long flags)172aab6724cSSimon Glass phys_addr_t pci_hose_bus_to_phys(struct pci_controller *hose,
173aab6724cSSimon Glass pci_addr_t bus_addr,
174aab6724cSSimon Glass unsigned long flags)
175aab6724cSSimon Glass {
176aab6724cSSimon Glass phys_addr_t phys_addr = 0;
177aab6724cSSimon Glass int ret;
178aab6724cSSimon Glass
179aab6724cSSimon Glass if (!hose) {
180aab6724cSSimon Glass puts("pci_hose_bus_to_phys: invalid hose\n");
181aab6724cSSimon Glass return phys_addr;
182aab6724cSSimon Glass }
183aab6724cSSimon Glass
184aab6724cSSimon Glass /*
185aab6724cSSimon Glass * if PCI_REGION_MEM is set we do a two pass search with preference
186aab6724cSSimon Glass * on matches that don't have PCI_REGION_SYS_MEMORY set
187aab6724cSSimon Glass */
18835262850SCheng Gu if ((flags & PCI_REGION_TYPE) == PCI_REGION_MEM) {
189aab6724cSSimon Glass ret = __pci_hose_bus_to_phys(hose, bus_addr,
190aab6724cSSimon Glass flags, PCI_REGION_SYS_MEMORY, &phys_addr);
191aab6724cSSimon Glass if (!ret)
192aab6724cSSimon Glass return phys_addr;
193aab6724cSSimon Glass }
194aab6724cSSimon Glass
195aab6724cSSimon Glass ret = __pci_hose_bus_to_phys(hose, bus_addr, flags, 0, &phys_addr);
196aab6724cSSimon Glass
197aab6724cSSimon Glass if (ret)
198aab6724cSSimon Glass puts("pci_hose_bus_to_phys: invalid physical address\n");
199aab6724cSSimon Glass
200aab6724cSSimon Glass return phys_addr;
201238fe16cSBin Meng }
202238fe16cSBin Meng
__pci_hose_phys_to_bus(struct pci_controller * hose,phys_addr_t phys_addr,unsigned long flags,unsigned long skip_mask,pci_addr_t * ba)203238fe16cSBin Meng int __pci_hose_phys_to_bus(struct pci_controller *hose,
204238fe16cSBin Meng phys_addr_t phys_addr,
205238fe16cSBin Meng unsigned long flags,
206238fe16cSBin Meng unsigned long skip_mask,
207238fe16cSBin Meng pci_addr_t *ba)
208238fe16cSBin Meng {
209238fe16cSBin Meng struct pci_region *res;
210238fe16cSBin Meng pci_addr_t bus_addr;
211238fe16cSBin Meng int i;
212238fe16cSBin Meng
213238fe16cSBin Meng for (i = 0; i < hose->region_count; i++) {
214238fe16cSBin Meng res = &hose->regions[i];
215238fe16cSBin Meng
216238fe16cSBin Meng if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
217238fe16cSBin Meng continue;
218238fe16cSBin Meng
219238fe16cSBin Meng if (res->flags & skip_mask)
220238fe16cSBin Meng continue;
221238fe16cSBin Meng
222238fe16cSBin Meng bus_addr = phys_addr - res->phys_start + res->bus_start;
223238fe16cSBin Meng
224238fe16cSBin Meng if (bus_addr >= res->bus_start &&
225b4bd6554SMarcel Ziswiler (bus_addr - res->bus_start) < res->size) {
226238fe16cSBin Meng *ba = bus_addr;
227238fe16cSBin Meng return 0;
228238fe16cSBin Meng }
229238fe16cSBin Meng }
230238fe16cSBin Meng
231238fe16cSBin Meng return 1;
232238fe16cSBin Meng }
233238fe16cSBin Meng
234fcf45692SMinghuan Lian /*
235fcf45692SMinghuan Lian * pci_hose_phys_to_bus(): Convert physical address to bus address
236fcf45692SMinghuan Lian * @hose: PCI hose of the root PCI controller
237fcf45692SMinghuan Lian * @phys_addr: physical address to convert
238fcf45692SMinghuan Lian * @flags: flags of pci regions
239fcf45692SMinghuan Lian * @return bus address if OK, 0 on error
240fcf45692SMinghuan Lian */
pci_hose_phys_to_bus(struct pci_controller * hose,phys_addr_t phys_addr,unsigned long flags)241238fe16cSBin Meng pci_addr_t pci_hose_phys_to_bus(struct pci_controller *hose,
242238fe16cSBin Meng phys_addr_t phys_addr,
243238fe16cSBin Meng unsigned long flags)
244238fe16cSBin Meng {
245238fe16cSBin Meng pci_addr_t bus_addr = 0;
246238fe16cSBin Meng int ret;
247238fe16cSBin Meng
248238fe16cSBin Meng if (!hose) {
249238fe16cSBin Meng puts("pci_hose_phys_to_bus: invalid hose\n");
250238fe16cSBin Meng return bus_addr;
251238fe16cSBin Meng }
252238fe16cSBin Meng
253238fe16cSBin Meng /*
254238fe16cSBin Meng * if PCI_REGION_MEM is set we do a two pass search with preference
255238fe16cSBin Meng * on matches that don't have PCI_REGION_SYS_MEMORY set
256238fe16cSBin Meng */
25735262850SCheng Gu if ((flags & PCI_REGION_TYPE) == PCI_REGION_MEM) {
258238fe16cSBin Meng ret = __pci_hose_phys_to_bus(hose, phys_addr,
259238fe16cSBin Meng flags, PCI_REGION_SYS_MEMORY, &bus_addr);
260238fe16cSBin Meng if (!ret)
261238fe16cSBin Meng return bus_addr;
262238fe16cSBin Meng }
263238fe16cSBin Meng
264238fe16cSBin Meng ret = __pci_hose_phys_to_bus(hose, phys_addr, flags, 0, &bus_addr);
265238fe16cSBin Meng
266238fe16cSBin Meng if (ret)
267238fe16cSBin Meng puts("pci_hose_phys_to_bus: invalid physical address\n");
268238fe16cSBin Meng
269238fe16cSBin Meng return bus_addr;
270aab6724cSSimon Glass }
271aab6724cSSimon Glass
pci_find_device(unsigned int vendor,unsigned int device,int index)272aab6724cSSimon Glass pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index)
273aab6724cSSimon Glass {
274aab6724cSSimon Glass struct pci_device_id ids[2] = { {}, {0, 0} };
275aab6724cSSimon Glass
276aab6724cSSimon Glass ids[0].vendor = vendor;
277aab6724cSSimon Glass ids[0].device = device;
278aab6724cSSimon Glass
279aab6724cSSimon Glass return pci_find_devices(ids, index);
280aab6724cSSimon Glass }
281aab6724cSSimon Glass
pci_hose_find_devices(struct pci_controller * hose,int busnum,struct pci_device_id * ids,int * indexp)282aab6724cSSimon Glass pci_dev_t pci_hose_find_devices(struct pci_controller *hose, int busnum,
283aab6724cSSimon Glass struct pci_device_id *ids, int *indexp)
284aab6724cSSimon Glass {
285aab6724cSSimon Glass int found_multi = 0;
286aab6724cSSimon Glass u16 vendor, device;
287aab6724cSSimon Glass u8 header_type;
288aab6724cSSimon Glass pci_dev_t bdf;
289aab6724cSSimon Glass int i;
290aab6724cSSimon Glass
291aab6724cSSimon Glass for (bdf = PCI_BDF(busnum, 0, 0);
292aab6724cSSimon Glass bdf < PCI_BDF(busnum + 1, 0, 0);
293aab6724cSSimon Glass bdf += PCI_BDF(0, 0, 1)) {
294aab6724cSSimon Glass if (pci_skip_dev(hose, bdf))
295aab6724cSSimon Glass continue;
296aab6724cSSimon Glass
297aab6724cSSimon Glass if (!PCI_FUNC(bdf)) {
298aab6724cSSimon Glass pci_read_config_byte(bdf, PCI_HEADER_TYPE,
299aab6724cSSimon Glass &header_type);
300aab6724cSSimon Glass found_multi = header_type & 0x80;
301aab6724cSSimon Glass } else {
302aab6724cSSimon Glass if (!found_multi)
303aab6724cSSimon Glass continue;
304aab6724cSSimon Glass }
305aab6724cSSimon Glass
306aab6724cSSimon Glass pci_read_config_word(bdf, PCI_VENDOR_ID, &vendor);
307aab6724cSSimon Glass pci_read_config_word(bdf, PCI_DEVICE_ID, &device);
308aab6724cSSimon Glass
309aab6724cSSimon Glass for (i = 0; ids[i].vendor != 0; i++) {
310aab6724cSSimon Glass if (vendor == ids[i].vendor &&
311aab6724cSSimon Glass device == ids[i].device) {
312aab6724cSSimon Glass if ((*indexp) <= 0)
313aab6724cSSimon Glass return bdf;
314aab6724cSSimon Glass
315aab6724cSSimon Glass (*indexp)--;
316aab6724cSSimon Glass }
317aab6724cSSimon Glass }
318aab6724cSSimon Glass }
319aab6724cSSimon Glass
320aab6724cSSimon Glass return -1;
321aab6724cSSimon Glass }
322170366c1SSimon Glass
pci_find_class(uint find_class,int index)323170366c1SSimon Glass pci_dev_t pci_find_class(uint find_class, int index)
324170366c1SSimon Glass {
325170366c1SSimon Glass int bus;
326170366c1SSimon Glass int devnum;
327170366c1SSimon Glass pci_dev_t bdf;
328170366c1SSimon Glass uint32_t class;
329170366c1SSimon Glass
330170366c1SSimon Glass for (bus = 0; bus <= pci_last_busno(); bus++) {
331170366c1SSimon Glass for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES - 1; devnum++) {
332170366c1SSimon Glass pci_read_config_dword(PCI_BDF(bus, devnum, 0),
333170366c1SSimon Glass PCI_CLASS_REVISION, &class);
334170366c1SSimon Glass if (class >> 16 == 0xffff)
335170366c1SSimon Glass continue;
336170366c1SSimon Glass
337170366c1SSimon Glass for (bdf = PCI_BDF(bus, devnum, 0);
338170366c1SSimon Glass bdf <= PCI_BDF(bus, devnum,
339170366c1SSimon Glass PCI_MAX_PCI_FUNCTIONS - 1);
340170366c1SSimon Glass bdf += PCI_BDF(0, 0, 1)) {
341170366c1SSimon Glass pci_read_config_dword(bdf, PCI_CLASS_REVISION,
342170366c1SSimon Glass &class);
343170366c1SSimon Glass class >>= 8;
344170366c1SSimon Glass
345170366c1SSimon Glass if (class != find_class)
346170366c1SSimon Glass continue;
347170366c1SSimon Glass /*
348170366c1SSimon Glass * Decrement the index. We want to return the
349170366c1SSimon Glass * correct device, so index is 0 for the first
350170366c1SSimon Glass * matching device, 1 for the second, etc.
351170366c1SSimon Glass */
352170366c1SSimon Glass if (index) {
353170366c1SSimon Glass index--;
354170366c1SSimon Glass continue;
355170366c1SSimon Glass }
356170366c1SSimon Glass /* Return index'th controller. */
357170366c1SSimon Glass return bdf;
358170366c1SSimon Glass }
359170366c1SSimon Glass }
360170366c1SSimon Glass }
361170366c1SSimon Glass
362170366c1SSimon Glass return -ENODEV;
363170366c1SSimon Glass }
3640fe9cb0fSSimon Glass #endif /* !CONFIG_DM_PCI || CONFIG_DM_PCI_COMPAT */
365