/* * Copyright (c) 2009-2011, 2013-2014 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief PCI bus support * * * This module implements the PCI H/W access functions. */ #include #include #include #include #include #if (PCI_CTRL_ADDR_REG == 0) #error "PCI_CTRL_ADDR_REG cannot be zero" #endif #if (PCI_CTRL_DATA_REG == 0) #error "PCI_CTRL_DATA_REG cannot be zero" #endif /** * * @brief Read a PCI controller register * * @param reg PCI register to read * @param data where to put the data * @param size size of the data to read (8/16/32 bits) * * This routine reads the specified register from the PCI controller and * places the data into the provided buffer. * * @return N/A * */ static void pci_ctrl_read(uint32_t reg, uint32_t *data, uint32_t size) { /* read based on the size requested */ switch (size) { /* long (32 bits) */ case SYS_PCI_ACCESS_32BIT: *data = sys_in32(reg); break; /* word (16 bits) */ case SYS_PCI_ACCESS_16BIT: *data = sys_in16(reg); break; /* byte (8 bits) */ case SYS_PCI_ACCESS_8BIT: *data = sys_in8(reg); break; } } /** * * @brief Write a PCI controller register * * @param reg PCI register to write * @param data data to write * @param size size of the data to write (8/16/32 bits) * * This routine writes the provided data to the specified register in the PCI * controller. * * @return N/A * */ static void pci_ctrl_write(uint32_t reg, uint32_t data, uint32_t size) { /* write based on the size requested */ switch (size) { /* long (32 bits) */ case SYS_PCI_ACCESS_32BIT: sys_out32(data, reg); break; /* word (16 bits) */ case SYS_PCI_ACCESS_16BIT: sys_out16(data, reg); break; /* byte (8 bits) */ case SYS_PCI_ACCESS_8BIT: sys_out8(data, reg); break; } } /** * * @brief Read the PCI controller data register * * @param controller controller number * @param offset is the offset within the data region * @param data is the returned data * @param size is the size of the data to read * * This routine reads the data register of the specified PCI controller. * * @return 0 or -1 * */ static int pci_ctrl_data_read(uint32_t controller, uint32_t offset, uint32_t *data, uint32_t size) { /* we only support one controller */ if (controller != DEFAULT_PCI_CONTROLLER) { return (-1); } pci_ctrl_read(PCI_CTRL_DATA_REG + offset, data, size); return 0; } /** * * @brief Write the PCI controller data register * * @param controller the controller number * @param offset is the offset within the address register * @param data is the data to write * @param size is the size of the data * * This routine writes the provided data to the data register of the * specified PCI controller. * * @return 0 or -1 * */ static int pci_ctrl_data_write(uint32_t controller, uint32_t offset, uint32_t data, uint32_t size) { /* we only support one controller */ if (controller != DEFAULT_PCI_CONTROLLER) { return (-1); } pci_ctrl_write(PCI_CTRL_DATA_REG + offset, data, size); return 0; } /** * * @brief Write the PCI controller address register * * @param controller is the controller number * @param offset is the offset within the address register * @param data is the data to write * @param size is the size of the data * * This routine writes the provided data to the address register of the * specified PCI controller. * * @return 0 or -1 * */ static int pci_ctrl_addr_write(uint32_t controller, uint32_t offset, uint32_t data, uint32_t size) { /* we only support one controller */ if (controller != DEFAULT_PCI_CONTROLLER) { return (-1); } pci_ctrl_write(PCI_CTRL_ADDR_REG + offset, data, size); return 0; } /** * * @brief Read a PCI register from a device * * This routine reads data from a PCI device's configuration space. The * device and register to read is specified by the address parameter ("addr") * and must be set appropriately by the caller. The address is defined by * the structure type pci_addr_t and contains the following members: * * bus: PCI bus number (0-255) * device: PCI device number (0-31) * func: device function number (0-7) * reg: device 32-bit register number to read (0-63) * offset: offset within 32-bit register to read (0-3) * * The size parameter specifies the number of bytes to read from the PCI * configuration space, valid values are 1, 2, and 4 bytes. A 32-bit value * is always returned but it will contain only the number of bytes specified * by the size parameter. * * If multiple PCI controllers are present in the system, the controller id * can be specified in the "controller" parameter. If only one controller * is present, the id DEFAULT_PCI_CONTROLLER can be used to denote this * controller. * * Example: * * union pci_addr_reg addr; * uint32_t status; * * addr.field.bus = 0; /@ PCI bus zero @/ * addr.field.device = 1; /@ PCI device one @/ * addr.field.func = 0; /@ PCI function zero @/ * addr.field.reg = 4; /@ PCI register 4 @/ * addr.field.offset = 0; /@ PCI register offset @/ * * pci_read (DEFAULT_PCI_CONTROLLER, addr, sizeof(uint16_t), &status); * * * NOTE: * Reading of PCI data must be performed as an atomic operation. It is up to * the caller to enforce this. * * @param controller is the PCI controller number to use * @param addr is the PCI address to read * @param size is the size of the data in bytes * @param data is a pointer to the data read from the device * * @return N/A * */ void pci_read(uint32_t controller, union pci_addr_reg addr, uint32_t size, uint32_t *data) { uint32_t access_size; uint32_t access_offset; /* validate the access size */ switch (size) { case 1: access_size = SYS_PCI_ACCESS_8BIT; access_offset = addr.field.offset; break; case 2: access_size = SYS_PCI_ACCESS_16BIT; access_offset = addr.field.offset; break; case 4: default: access_size = SYS_PCI_ACCESS_32BIT; access_offset = 0; break; } /* ensure enable has been set */ addr.field.enable = 1; /* clear the offset for the address register */ addr.field.offset = 0; /* read the data from the PCI controller */ pci_ctrl_addr_write( controller, PCI_NO_OFFSET, addr.value, SYS_PCI_ACCESS_32BIT); pci_ctrl_data_read(controller, access_offset, data, access_size); } /** * * @brief Write a to a PCI register * * This routine writes data to a PCI device's configuration space. The * device and register to write is specified by the address parameter ("addr") * and must be set appropriately by the caller. The address is defined by * the structure type pci_addr_t and contains the following members: * * bus: PCI bus number (0-255) * device: PCI device number (0-31) * func: device function number (0-7) * reg: device register number to read (0-63) * offset: offset within 32-bit register to write (0-3) * * The size parameter specifies the number of bytes to write to the PCI * configuration space, valid values are 1, 2, and 4 bytes. A 32-bit value * is always provided but only the number of bytes specified by the size * parameter will be written to the device. * * If multiple PCI controllers are present in the system, the controller id * can be specified in the "controller" parameter. If only one controller * is present, the id DEFAULT_PCI_CONTROLLER can be used to denote this * controller. * * Example: * * pci_addr_t addr; * uint32_t bar0 = 0xE0000000; * * addr.field.bus = 0; /@ PCI bus zero @/ * addr.field.device = 1; /@ PCI device one @/ * addr.field.func = 0; /@ PCI function zero @/ * addr.field.reg = 16; /@ PCI register 16 @/ * addr.field.offset = 0; /@ PCI register offset @/ * * pci_write (DEFAULT_PCI_CONTROLLER, addr, sizeof(uint32_t), bar0); * * NOTE: * Writing of PCI data must be performed as an atomic operation. It is up to * the caller to enforce this. * * @param controller is the PCI controller to use * @param addr is the PCI addres to read * @param size is the size in bytes to write * @param data is the data to write * * @return N/A * */ void pci_write(uint32_t controller, union pci_addr_reg addr, uint32_t size, uint32_t data) { uint32_t access_size; uint32_t access_offset; /* validate the access size */ switch (size) { case 1: access_size = SYS_PCI_ACCESS_8BIT; access_offset = addr.field.offset; break; case 2: access_size = SYS_PCI_ACCESS_16BIT; access_offset = addr.field.offset; break; case 4: default: access_size = SYS_PCI_ACCESS_32BIT; access_offset = 0; break; } /* ensure enable has been set */ addr.field.enable = 1; /* clear the offset for the address register */ addr.field.offset = 0; /* write the data to the PCI controller */ pci_ctrl_addr_write( controller, PCI_NO_OFFSET, addr.value, SYS_PCI_ACCESS_32BIT); pci_ctrl_data_write(controller, access_offset, data, access_size); } /** * * @brief Get the PCI header for a device * * This routine reads the PCI header for the specified device and puts the * result in the supplied header structure. * * @return N/A */ void pci_header_get(uint32_t controller, union pci_addr_reg pci_ctrl_addr, union pci_dev *pci_dev_header) { uint32_t i; /* clear out the header */ memset(pci_dev_header, 0, sizeof(*pci_dev_header)); /* fill in the PCI header from the device */ for (i = 0; i < PCI_HEADER_WORDS; i++) { pci_ctrl_addr.field.reg = i; pci_read(controller, pci_ctrl_addr, sizeof(uint32_t), &pci_dev_header->words.word[i]); } }