| /* pci.c - PCI probe and information routines */ |
| |
| /* |
| * Copyright (c) 2013-2014 Wind River Systems, Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1) Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2) Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * 3) Neither the name of Wind River Systems nor the names of its contributors |
| * may be used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* |
| DESCRIPTION |
| Module implements routines for PCI bus initialization and query. |
| Note that the BSP must call pci_bus_scan() before any other PCI |
| API is called. |
| |
| USAGE |
| In order to use the driver, BSP has to define: |
| - Register addresses: |
| - PCI_CTRL_ADDR_REG; |
| - PCI_CTRL_DATA_REG; |
| - Register read/write routines: |
| - PLB_LONG_REG_READ() / PLB_LONG_REG_WRITE(); |
| - PLB_WORD_REG_READ() / PLB_WORD_REG_WRITE(); |
| - PLB_BYTE_REG_READ() / PLB_BYTE_REG_WRITE(); |
| - pci_pin2irq() - the routine that converts the PCI interrupt pin |
| number to IRQ number. |
| */ |
| |
| #include <nanokernel.h> |
| #include <nanokernel/cpu.h> |
| #include <misc/printk.h> |
| #include <toolchain.h> |
| #include <sections.h> |
| |
| #include <board.h> |
| |
| #include "pci_mgr.h" |
| #include "pci.h" |
| |
| /* NOTE. These parameters may need to be configurable */ |
| #define LSPCI_MAX_BUS 256 /* maximum number of buses to scan */ |
| #define LSPCI_MAX_DEV 32 /* maximum number of devices to scan */ |
| #define LSPCI_MAX_FUNC 8 /* maximum device functions to scan */ |
| #define LSPCI_MAX_REG 64 /* maximum device registers to read */ |
| |
| /* Base Address Register configuration fields */ |
| |
| #define BAR_SPACE(x) ((x) & 0x00000001) |
| |
| #define BAR_TYPE(x) ((x) & 0x00000006) |
| #define BAR_TYPE_32BIT 0 |
| #define BAR_TYPE_64BIT 4 |
| |
| #define BAR_PREFETCH(x) (((x) >> 3) & 0x00000001) |
| #define BAR_ADDR(x) (((x) >> 4) & 0x0fffffff) |
| |
| #define BAR_IO_MASK(x) ((x) & ~0x3) |
| #define BAR_MEM_MASK(x) ((x) & ~0xf) |
| |
| #define MAX_BARS 6 |
| |
| static struct pci_dev_info __noinit dev_info[CONFIG_MAX_PCI_DEVS]; |
| static int dev_info_idx = 0; |
| |
| /******************************************************************************* |
| * |
| * pci_get_bar_config - return the configuration for the specified BAR |
| * |
| * RETURNS: 0 if BAR is implemented, -1 if not. |
| */ |
| |
| static int pci_bar_config_get(uint32_t bus, |
| uint32_t dev, |
| uint32_t func, |
| uint32_t bar, |
| uint32_t *config) |
| { |
| union pci_addr_reg pci_ctrl_addr; |
| uint32_t old_value; |
| |
| pci_ctrl_addr.value = 0; |
| pci_ctrl_addr.field.enable = 1; |
| pci_ctrl_addr.field.bus = bus; |
| pci_ctrl_addr.field.device = dev; |
| pci_ctrl_addr.field.func = func; |
| pci_ctrl_addr.field.reg = 4 + bar; |
| |
| /* save the current setting */ |
| |
| pci_read(DEFAULT_PCI_CONTROLLER, |
| pci_ctrl_addr, |
| sizeof(old_value), |
| &old_value); |
| |
| /* write to the BAR to see how large it is */ |
| pci_write(DEFAULT_PCI_CONTROLLER, |
| pci_ctrl_addr, |
| sizeof(uint32_t), |
| 0xffffffff); |
| pci_read(DEFAULT_PCI_CONTROLLER, pci_ctrl_addr, sizeof(*config), config); |
| |
| /* put back the old configuration */ |
| |
| pci_write(DEFAULT_PCI_CONTROLLER, |
| pci_ctrl_addr, |
| sizeof(old_value), |
| old_value); |
| |
| /* check if this BAR is implemented */ |
| if (*config != 0xffffffff && *config != 0) |
| return 0; |
| |
| /* BAR not supported */ |
| |
| return -1; |
| } |
| |
| /******************************************************************************* |
| * |
| * pci_bar_params_get - retrieve the I/O address and IRQ of the specified BAR |
| * |
| * RETURN: -1 on error, 0 if 32 bit BAR retrieved or 1 if 64 bit BAR retrieved |
| * |
| * NOTE: Routine does not set up parameters for 64 bit BARS, they are ignored. |
| * |
| * \NOMANUAL |
| */ |
| |
| static inline int pci_bar_params_get(uint32_t bus, |
| uint32_t dev, |
| uint32_t func, |
| uint32_t bar, |
| struct pci_dev_info *dev_info) |
| { |
| static union pci_addr_reg pci_ctrl_addr; |
| uint32_t bar_value; |
| uint32_t bar_config; |
| uint32_t addr; |
| uint32_t mask; |
| |
| pci_ctrl_addr.value = 0; |
| pci_ctrl_addr.field.enable = 1; |
| pci_ctrl_addr.field.bus = bus; |
| pci_ctrl_addr.field.device = dev; |
| pci_ctrl_addr.field.func = func; |
| pci_ctrl_addr.field.reg = 4 + bar; |
| |
| pci_read(DEFAULT_PCI_CONTROLLER, |
| pci_ctrl_addr, |
| sizeof(bar_value), |
| &bar_value); |
| if (pci_bar_config_get(bus, dev, func, bar, &bar_config) != 0) |
| return -1; |
| |
| if (BAR_SPACE(bar_config) == BAR_SPACE_MEM) { |
| dev_info->mem_type = BAR_SPACE_MEM; |
| mask = ~0xf; |
| if (bar < 5 && BAR_TYPE(bar_config) == BAR_TYPE_64BIT) |
| return 1; /* 64-bit MEM */ |
| } else { |
| dev_info->mem_type = BAR_SPACE_IO; |
| mask = ~0x3; |
| } |
| |
| dev_info->addr = bar_value & mask; |
| |
| addr = bar_config & mask; |
| if (addr != 0) { |
| /* calculate the size of the BAR memory required */ |
| dev_info->size = 1 << (find_first_set_inline(addr) - 1); |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * |
| * pci_dev_scan - scan the specified PCI device for all sub functions |
| * |
| * RETURNS: N/A |
| */ |
| |
| static void pci_dev_scan(uint32_t bus, |
| uint32_t dev, |
| uint32_t class_mask /* bitmask, bits set for each needed |
| class */ |
| ) |
| { |
| uint32_t func; |
| uint32_t pci_data; |
| static union pci_addr_reg pci_ctrl_addr; |
| static union pci_dev pci_dev_header; |
| int i; |
| int max_bars; |
| |
| if (dev_info_idx == CONFIG_MAX_PCI_DEVS) { |
| /* No more room in the table */ |
| return; |
| } |
| |
| /* initialise the PCI controller address register value */ |
| |
| pci_ctrl_addr.value = 0; |
| pci_ctrl_addr.field.enable = 1; |
| pci_ctrl_addr.field.bus = bus; |
| pci_ctrl_addr.field.device = dev; |
| |
| /* scan all the possible functions for this device */ |
| |
| for (func = 0; func < LSPCI_MAX_FUNC; func++) { |
| pci_ctrl_addr.field.func = func; |
| pci_read(DEFAULT_PCI_CONTROLLER, |
| pci_ctrl_addr, |
| sizeof(pci_data), |
| &pci_data); |
| |
| if (pci_data == 0xffffffff) |
| continue; |
| |
| /* get the PCI header from the device */ |
| pci_header_get( |
| DEFAULT_PCI_CONTROLLER, bus, dev, func, &pci_dev_header); |
| |
| /* Skip a device if it's class is not specified by the caller */ |
| if (!((1 << pci_dev_header.field.class) & class_mask)) |
| continue; |
| |
| /* Get memory and interrupt information */ |
| if ((pci_dev_header.field.hdr_type & 0x7f) == 1) |
| max_bars = 2; |
| else |
| max_bars = MAX_BARS; |
| for (i = 0; i < max_bars; ++i) { |
| /* Ignore BARs with errors and 64 bit BARs */ |
| if (pci_bar_params_get( |
| bus, dev, func, i, dev_info + dev_info_idx) != |
| 0) |
| continue; |
| else { |
| dev_info[dev_info_idx].vendor_id = |
| pci_dev_header.field.vendor_id; |
| dev_info[dev_info_idx].device_id = |
| pci_dev_header.field.device_id; |
| dev_info[dev_info_idx].class = |
| pci_dev_header.field.class; |
| dev_info[dev_info_idx].irq = pci_pin2irq( |
| pci_dev_header.field.interrupt_pin); |
| dev_info_idx++; |
| if (dev_info_idx == CONFIG_MAX_PCI_DEVS) { |
| /* No more room in the table */ |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * pci_bus_scan - scans PCI bus for devices |
| * |
| * The routine scans the PCI bus for the devices, which classes are provided |
| * in the classMask argument. |
| * classMask is constructed as: |
| * (1 << class1) | (1 << class2) | ... | (1 << classN) |
| * |
| * \NOMANUAL |
| */ |
| |
| void pci_bus_scan(uint32_t class_mask /* bitmask, bits set for each needed class */ |
| ) |
| { |
| uint32_t bus; |
| uint32_t dev; |
| union pci_addr_reg pci_ctrl_addr; |
| uint32_t pci_data; |
| |
| /* initialise the PCI controller address register value */ |
| pci_ctrl_addr.value = 0; |
| pci_ctrl_addr.field.enable = 1; |
| |
| /* run through the buses and devices */ |
| for (bus = 0; bus < LSPCI_MAX_BUS; bus++) { |
| for (dev = 0; (dev < LSPCI_MAX_DEV) && |
| (dev_info_idx < CONFIG_MAX_PCI_DEVS); |
| dev++) { |
| pci_ctrl_addr.field.bus = bus; |
| pci_ctrl_addr.field.device = dev; |
| /* try and read register zero of the first function */ |
| pci_read(DEFAULT_PCI_CONTROLLER, |
| pci_ctrl_addr, |
| sizeof(pci_data), |
| &pci_data); |
| |
| /* scan the device if we found something */ |
| if (pci_data != 0xffffffff) |
| pci_dev_scan(bus, dev, class_mask); |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * pci_info_get - returns list of PCI devices |
| * |
| * \NOMANUAL |
| */ |
| struct pci_dev_info *pci_info_get(void) |
| { |
| return dev_info; |
| } |
| |
| /******************************************************************************* |
| * |
| * pci_dev_find - find PCI device of a specified class and specified index |
| * |
| * Routine looks through the list of detected PCI devices and if the device |
| * of specified <class> and <index> exists, sets up <address>, <size> and <irq>. |
| * |
| * This function can return error if the specified device is not found. In most |
| * cases the device class and index are known to exist and therefore will be |
| * found. However, due to the somewhat dynamic nature of PCI, we allow for the |
| * fact the device may not be found and another attempt with different |
| * parameters maybe made. |
| * |
| * RETURNS: 0, if device is found, -1 otherwise |
| */ |
| |
| int pci_dev_find(int class, int idx, uint32_t *addr, uint32_t *size, int *irq) |
| { |
| int i; |
| int j; |
| for (i = 0, j = 0; i < dev_info_idx; i++) { |
| if (dev_info[i].class != class) |
| continue; |
| |
| if (j == idx) { |
| *addr = dev_info[i].addr; |
| *size = dev_info[i].size; |
| *irq = dev_info[i].irq; |
| return 0; |
| } |
| j++; |
| } |
| return -1; |
| } |
| |
| #ifdef PCI_DEBUG |
| /******************************************************************************* |
| * |
| * pci_show - Show PCI devices |
| * |
| * Shows the PCI devices found. |
| * |
| * RETURNS: N/A |
| */ |
| |
| void pci_show(void) |
| { |
| int i; |
| |
| printk("PCI devices:\n"); |
| for (i = 0; i < dev_info_idx; i++) { |
| printk("%X:%X class: 0x%X, %s, addrs: 0x%X-0x%X, IRQ %d\n", |
| dev_info[i].vendor_id, |
| dev_info[i].device_id, |
| dev_info[i].class, |
| (dev_info[i].mem_type == BAR_SPACE_MEM) ? "MEM" : "I/O", |
| (uint32_t)dev_info[i].addr, |
| (uint32_t)(dev_info[i].addr + dev_info[i].size - 1), |
| dev_info[i].irq); |
| } |
| } |
| #endif /* PCI_DEBUG */ |