blob: b5812f372dd9f1f8c2b4b791c1bad6272e481ba4 [file] [log] [blame]
/*
* 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 <kernel.h>
#include <arch/cpu.h>
#include <pci/pci_mgr.h>
#include <string.h>
#include <board.h>
#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]);
}
}