diff options
Diffstat (limited to 'include/pci.h')
-rw-r--r-- | include/pci.h | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/include/pci.h b/include/pci.h new file mode 100644 index 0000000..984bd38 --- /dev/null +++ b/include/pci.h @@ -0,0 +1,504 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PCI_H +#define __PCI_H + +#include <opal.h> +#include <device.h> +#include <ccan/list/list.h> + +/* PCI Slot Info: Wired Lane Values + * + * Values 0 to 6 match slot map 1005. In case of *any* change here + * make sure to keep the lxvpd.c parsing code in sync *and* the + * corresponding label strings in pci.c + */ +#define PCI_SLOT_WIRED_LANES_UNKNOWN 0x00 +#define PCI_SLOT_WIRED_LANES_PCIE_X1 0x01 +#define PCI_SLOT_WIRED_LANES_PCIE_X2 0x02 +#define PCI_SLOT_WIRED_LANES_PCIE_X4 0x03 +#define PCI_SLOT_WIRED_LANES_PCIE_X8 0x04 +#define PCI_SLOT_WIRED_LANES_PCIE_X16 0x05 +#define PCI_SLOT_WIRED_LANES_PCIE_X32 0x06 +#define PCI_SLOT_WIRED_LANES_PCIX_32 0x07 +#define PCI_SLOT_WIRED_LANES_PCIX_64 0x08 + +/* PCI Slot Info: Bus Clock Values */ +#define PCI_SLOT_BUS_CLK_RESERVED 0x00 +#define PCI_SLOT_BUS_CLK_GEN_1 0x01 +#define PCI_SLOT_BUS_CLK_GEN_2 0x02 +#define PCI_SLOT_BUS_CLK_GEN_3 0x03 + +/* PCI Slot Info: Connector Type Values */ +#define PCI_SLOT_CONNECTOR_PCIE_EMBED 0x00 +#define PCI_SLOT_CONNECTOR_PCIE_X1 0x01 +#define PCI_SLOT_CONNECTOR_PCIE_X2 0x02 +#define PCI_SLOT_CONNECTOR_PCIE_X4 0x03 +#define PCI_SLOT_CONNECTOR_PCIE_X8 0x04 +#define PCI_SLOT_CONNECTOR_PCIE_X16 0x05 +#define PCI_SLOT_CONNECTOR_PCIE_NS 0x0E /* Non-Standard */ + +/* PCI Slot Info: Card Description Values */ +#define PCI_SLOT_DESC_NON_STANDARD 0x00 /* Embed/Non-Standard Connector */ +#define PCI_SLOT_DESC_PCIE_FH_FL 0x00 /* Full Height, Full Length */ +#define PCI_SLOT_DESC_PCIE_FH_HL 0x01 /* Full Height, Half Length */ +#define PCI_SLOT_DESC_PCIE_HH_FL 0x02 /* Half Height, Full Length */ +#define PCI_SLOT_DESC_PCIE_HH_HL 0x03 /* Half Height, Half Length */ + +/* PCI Slot Info: Mechanicals Values */ +#define PCI_SLOT_MECH_NONE 0x00 +#define PCI_SLOT_MECH_RIGHT 0x01 +#define PCI_SLOT_MECH_LEFT 0x02 +#define PCI_SLOT_MECH_RIGHT_LEFT 0x03 + +/* PCI Slot Info: Power LED Control Values */ +#define PCI_SLOT_PWR_LED_CTL_NONE 0x00 /* No Control */ +#define PCI_SLOT_PWR_LED_CTL_FSP 0x01 /* FSP Controlled */ +#define PCI_SLOT_PWR_LED_CTL_KERNEL 0x02 /* Kernel Controlled */ + +/* PCI Slot Info: ATTN LED Control Values */ +#define PCI_SLOT_ATTN_LED_CTL_NONE 0x00 /* No Control */ +#define PCI_SLOT_ATTN_LED_CTL_FSP 0x01 /* FSP Controlled */ +#define PCI_SLOT_ATTN_LED_CTL_KERNEL 0x02 /* Kernel Controlled */ + +/* PCI Slot Entry Information */ +struct pci_slot_info { + uint8_t switch_id; + uint8_t vswitch_id; + uint8_t dev_id; + char label[9]; + bool pluggable; + bool power_ctl; + uint8_t wired_lanes; + uint8_t bus_clock; + uint8_t connector_type; + uint8_t card_desc; + uint8_t card_mech; + uint8_t pwr_led_ctl; + uint8_t attn_led_ctl; + uint8_t slot_index; +}; + +/* + * While this might not be necessary in the long run, the existing + * Linux kernels expect us to provide a device-tree that contains + * a representation of all PCI devices below the host bridge. Thus + * we need to perform a bus scan. We don't need to assign MMIO/IO + * resources, but we do need to assign bus numbers in a way that + * is going to be compatible with the HW constraints for PE filtering + * that is naturally aligned power of twos for ranges below a bridge. + * + * Thus the structure pci_device is used for the tracking of the + * detected devices and the later generation of the device-tree. + * + * We do not keep a separate structure for a bus, however a device + * can have children in which case a device is a bridge. + * + * Because this is likely to change, we avoid putting too much + * information in that structure nor relying on it for anything + * else but the construction of the flat device-tree. + */ +struct pci_device { + uint16_t bdfn; + bool is_bridge; + bool is_multifunction; + uint8_t dev_type; /* PCIE */ + uint32_t scan_map; + + uint64_t cap_list; + uint32_t cap[64]; + uint32_t mps; /* Max payload size capability */ + + struct pci_slot_info *slot_info; + struct pci_device *parent; + struct list_head children; + struct list_node link; +}; + +static inline void pci_set_cap(struct pci_device *pd, + int id, int pos, bool ext) +{ + if (!ext) { + pd->cap_list |= (0x1ul << id); + pd->cap[id] = pos; + } else { + pd->cap_list |= (0x1ul << (id + 32)); + pd->cap[id + 32] = pos; + } +} + +static inline bool pci_has_cap(struct pci_device *pd, + int id, bool ext) +{ + if (!ext) + return !!(pd->cap_list & (0x1ul << id)); + else + return !!(pd->cap_list & (0x1ul << (id + 32))); +} + +static inline int pci_cap(struct pci_device *pd, + int id, bool ext) +{ + if (!ext) + return pd->cap[id]; + else + return pd->cap[id + 32]; +} + +/* + * When generating the device-tree, we need to keep track of + * the LSI mapping & swizzle it. This state structure is + * passed by the PHB to pci_add_nodes() and will be used + * internally. + * + * We assume that the interrupt parent (PIC) #address-cells + * is 0 and #interrupt-cells has a max value of 2. + */ +struct pci_lsi_state { +#define MAX_INT_SIZE 2 + uint32_t int_size; /* #cells */ + uint32_t int_val[4][MAX_INT_SIZE]; /* INTA...INTD */ + uint32_t int_parent[4]; +}; + +/* + * NOTE: All PCI functions return negative OPAL error codes + * + * In addition, some functions may return a positive timeout + * value or some other state information, see the description + * of individual functions. If nothing is specified, it's + * just an error code or 0 (success). + * + * Functions that operate asynchronously will return a positive + * delay value and will require the ->poll() op to be called after + * that delay. ->poll() will then return success, a negative error + * code, or another delay. + * + * Note: If an asynchronous function returns 0, it has completed + * successfully and does not require a call to ->poll(). Similarly + * if ->poll() is called while no operation is in progress, it will + * simply return 0 (success) + * + * Note that all functions except ->lock() itself assume that the + * caller is holding the PHB lock. + * + * TODO: Add more interfaces to control things like link width + * reduction for power savings etc... + */ + +struct phb; + +struct phb_ops { + /* + * Locking. This is called around OPAL accesses + */ + void (*lock)(struct phb *phb); + void (*unlock)(struct phb *phb); + + /* + * Config space ops + */ + int64_t (*cfg_read8)(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint8_t *data); + int64_t (*cfg_read16)(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint16_t *data); + int64_t (*cfg_read32)(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint32_t *data); + int64_t (*cfg_write8)(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint8_t data); + int64_t (*cfg_write16)(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint16_t data); + int64_t (*cfg_write32)(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint32_t data); + + /* + * Bus number selection. See pci_scan() for a description + */ + uint8_t (*choose_bus)(struct phb *phb, struct pci_device *bridge, + uint8_t candidate, uint8_t *max_bus, + bool *use_max); + + /* + * Device init method is called after a device has been detected + * and before probing further. It can alter things like scan_map + * for bridge ports etc... + */ + void (*device_init)(struct phb *phb, struct pci_device *device); + + /* + * EEH methods + * + * The various arguments are identical to the corresponding + * OPAL functions + */ + int64_t (*eeh_freeze_status)(struct phb *phb, uint64_t pe_number, + uint8_t *freeze_state, + uint16_t *pci_error_type, + uint16_t *severity, + uint64_t *phb_status); + int64_t (*eeh_freeze_clear)(struct phb *phb, uint64_t pe_number, + uint64_t eeh_action_token); + + int64_t (*get_diag_data)(struct phb *phb, void *diag_buffer, + uint64_t diag_buffer_len); + int64_t (*get_diag_data2)(struct phb *phb, void *diag_buffer, + uint64_t diag_buffer_len); + int64_t (*next_error)(struct phb *phb, uint64_t *first_frozen_pe, + uint16_t *pci_error_type, uint16_t *severity); + + /* + * Other IODA methods + * + * The various arguments are identical to the corresponding + * OPAL functions + */ + int64_t (*pci_reinit)(struct phb *phb, uint64_t scope, uint64_t data); + int64_t (*phb_mmio_enable)(struct phb *phb, uint16_t window_type, + uint16_t window_num, uint16_t enable); + + int64_t (*set_phb_mem_window)(struct phb *phb, uint16_t window_type, + uint16_t window_num, uint64_t addr, + uint64_t pci_addr, uint64_t size); + + int64_t (*map_pe_mmio_window)(struct phb *phb, uint16_t pe_number, + uint16_t window_type, uint16_t window_num, + uint16_t segment_num); + + int64_t (*set_pe)(struct phb *phb, uint64_t pe_number, + uint64_t bus_dev_func, uint8_t bus_compare, + uint8_t dev_compare, uint8_t func_compare, + uint8_t pe_action); + + int64_t (*set_peltv)(struct phb *phb, uint32_t parent_pe, + uint32_t child_pe, uint8_t state); + + int64_t (*map_pe_dma_window)(struct phb *phb, uint16_t pe_number, + uint16_t window_id, uint16_t tce_levels, + uint64_t tce_table_addr, + uint64_t tce_table_size, + uint64_t tce_page_size); + + int64_t (*map_pe_dma_window_real)(struct phb *phb, uint16_t pe_number, + uint16_t dma_window_number, + uint64_t pci_start_addr, + uint64_t pci_mem_size); + + int64_t (*set_mve)(struct phb *phb, uint32_t mve_number, + uint32_t pe_number); + + int64_t (*set_mve_enable)(struct phb *phb, uint32_t mve_number, + uint32_t state); + + int64_t (*set_xive_pe)(struct phb *phb, uint32_t pe_number, + uint32_t xive_num); + + int64_t (*get_xive_source)(struct phb *phb, uint32_t xive_num, + int32_t *interrupt_source_number); + + int64_t (*get_msi_32)(struct phb *phb, uint32_t mve_number, + uint32_t xive_num, uint8_t msi_range, + uint32_t *msi_address, uint32_t *message_data); + + int64_t (*get_msi_64)(struct phb *phb, uint32_t mve_number, + uint32_t xive_num, uint8_t msi_range, + uint64_t *msi_address, uint32_t *message_data); + + int64_t (*ioda_reset)(struct phb *phb, bool purge); + + /* + * P5IOC2 only + */ + int64_t (*set_phb_tce_memory)(struct phb *phb, uint64_t tce_mem_addr, + uint64_t tce_mem_size); + + /* + * IODA2 PCI interfaces + */ + int64_t (*pci_msi_eoi)(struct phb *phb, uint32_t hwirq); + + /* + * Slot control + */ + + /* presence_detect - Check for a present device + * + * Immediate return of: + * + * OPAL_SHPC_DEV_NOT_PRESENT = 0, + * OPAL_SHPC_DEV_PRESENT = 1 + * + * or a negative OPAL error code + */ + int64_t (*presence_detect)(struct phb *phb); + + /* link_state - Check link state + * + * Immediate return of: + * + * OPAL_SHPC_LINK_DOWN = 0, + * OPAL_SHPC_LINK_UP_x1 = 1, + * OPAL_SHPC_LINK_UP_x2 = 2, + * OPAL_SHPC_LINK_UP_x4 = 4, + * OPAL_SHPC_LINK_UP_x8 = 8, + * OPAL_SHPC_LINK_UP_x16 = 16, + * OPAL_SHPC_LINK_UP_x32 = 32 + * + * or a negative OPAL error code + */ + int64_t (*link_state)(struct phb *phb); + + /* power_state - Check slot power state + * + * Immediate return of: + * + * OPAL_SLOT_POWER_OFF = 0, + * OPAL_SLOT_POWER_ON = 1, + * + * or a negative OPAL error code + */ + int64_t (*power_state)(struct phb *phb); + + /* slot_power_off - Start slot power off sequence + * + * Asynchronous function, returns a positive delay + * or a negative error code + */ + int64_t (*slot_power_off)(struct phb *phb); + + /* slot_power_on - Start slot power on sequence + * + * Asynchronous function, returns a positive delay + * or a negative error code. + */ + int64_t (*slot_power_on)(struct phb *phb); + + /* PHB power off and on after complete init */ + int64_t (*complete_reset)(struct phb *phb, uint8_t assert); + + /* hot_reset - Hot Reset sequence */ + int64_t (*hot_reset)(struct phb *phb); + + /* Fundamental reset */ + int64_t (*fundamental_reset)(struct phb *phb); + + /* poll - Poll and advance asynchronous operations + * + * Returns a positive delay, 0 for success or a + * negative OPAL error code + */ + int64_t (*poll)(struct phb *phb); + + /* Put phb in capi mode or pcie mode */ + int64_t (*set_capi_mode)(struct phb *phb, uint64_t mode, uint64_t pe_number); +}; + +enum phb_type { + phb_type_pci, + phb_type_pcix_v1, + phb_type_pcix_v2, + phb_type_pcie_v1, + phb_type_pcie_v2, + phb_type_pcie_v3, +}; + +struct phb { + struct dt_node *dt_node; + int opal_id; + uint32_t scan_map; + enum phb_type phb_type; + struct list_head devices; + const struct phb_ops *ops; + struct pci_lsi_state lstate; + uint32_t mps; + + /* PCI-X only slot info, for PCI-E this is in the RC bridge */ + struct pci_slot_info *slot_info; + + /* Base location code used to generate the children one */ + const char *base_loc_code; + + /* Additional data the platform might need to attach */ + void *platform_data; +}; + +/* Config space ops wrappers */ +static inline int64_t pci_cfg_read8(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint8_t *data) +{ + return phb->ops->cfg_read8(phb, bdfn, offset, data); +} + +static inline int64_t pci_cfg_read16(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint16_t *data) +{ + return phb->ops->cfg_read16(phb, bdfn, offset, data); +} + +static inline int64_t pci_cfg_read32(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint32_t *data) +{ + return phb->ops->cfg_read32(phb, bdfn, offset, data); +} + +static inline int64_t pci_cfg_write8(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint8_t data) +{ + return phb->ops->cfg_write8(phb, bdfn, offset, data); +} + +static inline int64_t pci_cfg_write16(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint16_t data) +{ + return phb->ops->cfg_write16(phb, bdfn, offset, data); +} + +static inline int64_t pci_cfg_write32(struct phb *phb, uint32_t bdfn, + uint32_t offset, uint32_t data) +{ + return phb->ops->cfg_write32(phb, bdfn, offset, data); +} + +/* Utilities */ +extern int64_t pci_find_cap(struct phb *phb, uint16_t bdfn, uint8_t cap); +extern int64_t pci_find_ecap(struct phb *phb, uint16_t bdfn, uint16_t cap, + uint8_t *version); +extern int32_t pci_configure_mps(struct phb *phb, struct pci_device *pd); + +extern struct pci_device *pci_walk_dev(struct phb *phb, + int (*cb)(struct phb *, + struct pci_device *, + void *), + void *userdata); +extern struct pci_device *pci_find_dev(struct phb *phb, uint16_t bdfn); + +/* Manage PHBs */ +extern int64_t pci_register_phb(struct phb *phb); +extern int64_t pci_unregister_phb(struct phb *phb); +extern struct phb *pci_get_phb(uint64_t phb_id); +static inline void pci_put_phb(struct phb *phb __unused) { } + +/* Device tree */ +extern void pci_std_swizzle_irq_map(struct dt_node *dt_node, + struct pci_device *pd, + struct pci_lsi_state *lstate, + uint8_t swizzle); + +/* Initialize all PCI slots */ +extern void pci_init_slots(void); +extern void pci_reset(void); + +#endif /* __PCI_H */ |