| /* Copyright (c) 2009, 2010 The Regents of the University of California |
| * See LICENSE for details. |
| * |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * Original by Paul Pearce <pearce@eecs.berkeley.edu> */ |
| |
| #pragma once |
| |
| #include <ros/common.h> |
| #include <sys/queue.h> |
| #include <atomic.h> |
| #include <arch/pci_regs.h> |
| |
| #define pci_debug(...) printk(__VA_ARGS__) |
| |
| #define PCI_CONFIG_ADDR 0xCF8 |
| #define PCI_CONFIG_DATA 0xCFC |
| #define INVALID_VENDOR_ID 0xFFFF |
| |
| /* TODO: gut this (when the IOAPIC is fixed) */ |
| #define INVALID_BUS 0xFFFF |
| |
| #define PCI_NOINT 0x00 |
| #define PCI_INTA 0x01 |
| #define PCI_INTB 0x02 |
| #define PCI_INTC 0x03 |
| #define PCI_INTD 0x04 |
| |
| /* PCI Register Config Space */ |
| #define PCI_DEV_VEND_REG 0x00 /* for the 32 bit read of dev/vend */ |
| #define PCI_VENDID_REG 0x00 |
| #define PCI_DEVID_REG 0x02 |
| #define PCI_CMD_REG 0x04 |
| #define PCI_STATUS_REG 0x06 |
| #define PCI_REVID_REG 0x08 |
| #define PCI_PROGIF_REG 0x09 |
| #define PCI_SUBCLASS_REG 0x0a |
| #define PCI_CLASS_REG 0x0b |
| #define PCI_CLSZ_REG 0x0c |
| #define PCI_LATTIM_REG 0x0d |
| #define PCI_HEADER_REG 0x0e |
| #define PCI_BIST_REG 0x0f |
| /* Config space for header type 0x00 (Standard) */ |
| #define PCI_BAR0_STD 0x10 |
| #define PCI_BAR1_STD 0x14 |
| #define PCI_BAR2_STD 0x18 |
| #define PCI_BAR3_STD 0x1c |
| #define PCI_BAR4_STD 0x20 |
| #define PCI_BAR5_STD 0x24 |
| #define PCI_BAR_OFF 0x04 |
| #define PCI_CARDBUS_STD 0x28 |
| #define PCI_SUBSYSVEN_STD 0x2c |
| #define PCI_SUBSYSID_STD 0x2e |
| #define PCI_EXPROM_STD 0x30 |
| #define PCI_CAPAB_STD 0x34 |
| #define PCI_IRQLINE_STD 0x3c |
| #define PCI_IRQPIN_STD 0x3d |
| #define PCI_MINGRNT_STD 0x3e |
| #define PCI_MAXLAT_STD 0x3f |
| /* Config space for header type 0x01 (PCI-PCI bridge) */ |
| /* None of these have been used, so if you use them, check them against |
| * http://wiki.osdev.org/PCI#PCI_Device_Structure */ |
| #define PCI_BAR0_BR 0x10 |
| #define PCI_BAR1_BR 0x14 |
| #define PCI_BUS1_BR 0x18 |
| #define PCI_BUS2_BR 0x19 |
| #define PCI_SUBBUS_BR 0x1a |
| #define PCI_LATTIM2_BR 0x1b |
| #define PCI_IOBASE_BR 0x1c |
| #define PCI_IOLIM_BR 0x1d |
| #define PCI_STATUS2_BR 0x1e |
| #define PCI_MEMBASE_BR 0x20 |
| #define PCI_MEMLIM_BR 0x22 |
| #define PCI_PREMEMBASE_BR 0x24 |
| #define PCI_PREMEMLIM_BR 0x26 |
| #define PCI_PREBASEUP32_BR 0x28 |
| #define PCI_PRELIMUP32_BR 0x2c |
| #define PCI_IOBASEUP16_BR 0x30 |
| #define PCI_IOLIMUP16_BR 0x32 |
| #define PCI_CAPAB_BR 0x34 |
| #define PCI_EXPROM_BR 0x38 |
| #define PCI_IRQLINE_BR 0x3c |
| #define PCI_IRQPIN_BR 0x3d |
| #define PCI_BDGCTL_BR 0x3e |
| /* Config space for header type 0x02 (PCI-Cardbus bridge) */ |
| /* None of these have been used, so if you use them, check them against |
| * http://wiki.osdev.org/PCI#PCI_Device_Structure */ |
| #define PCI_SOC_BASE_CB 0x10 |
| #define PCI_OFF_CAP_CB 0x14 |
| #define PCI_SEC_STAT_CB 0x16 |
| #define PCI_BUS_NR_CB 0x18 |
| #define PCI_CARDBUS_NR_CB 0x19 |
| #define PCI_SUBBUS_NR_CB 0x1a |
| #define PCI_CARD_LAT_CB 0x1b |
| #define PCI_MEM_BASE0_CB 0x1c |
| #define PCI_MEM_LIMIT0_CB 0x20 |
| #define PCI_MEM_BASE1_CB 0x24 |
| #define PCI_MEM_LIMIT1_CB 0x28 |
| #define PCI_IO_BASE0_CB 0x2c |
| #define PCI_IO_LIMIT0_CB 0x30 |
| #define PCI_IO_BASE1_CB 0x34 |
| #define PCI_IO_LIMIT1_CB 0x38 |
| #define PCI_IRQLINE_CB 0x3c |
| #define PCI_IRQPIN_CB 0x3d |
| #define PCI_BDGCTL_CB 0x3e |
| #define PCI_SUBDEVID_CB 0x40 |
| #define PCI_SUBVENID_CB 0x42 |
| #define PCI_16BIT_CB 0x44 |
| |
| /* Command Register Flags */ |
| #define PCI_CMD_IO_SPC (1 << 0) |
| #define PCI_CMD_MEM_SPC (1 << 1) |
| #define PCI_CMD_BUS_MAS (1 << 2) |
| #define PCI_CMD_SPC_CYC (1 << 3) |
| #define PCI_CMD_WR_EN (1 << 4) |
| #define PCI_CMD_VGA (1 << 5) |
| #define PCI_CMD_PAR_ERR (1 << 6) |
| /* #define PCI_CMD_XXX (1 << 7) Reserved */ |
| #define PCI_CMD_SERR (1 << 8) |
| #define PCI_CMD_FAST_EN (1 << 9) |
| #define PCI_CMD_IRQ_DIS (1 << 10) |
| |
| /* Status Register Flags (Bits 9 and 10 are one field) */ |
| /* Bits 0, 1, and 2 are reserved */ |
| #define PCI_ST_IRQ_STAT (1 << 3) |
| #define PCI_ST_CAP_LIST (1 << 4) |
| #define PCI_ST_66MHZ (1 << 5) |
| /* #define PCI_CMD_XXX (1 << 6) Reserved */ |
| #define PCI_ST_FAST_CAP (1 << 7) |
| #define PCI_ST_MASPAR_ERR (1 << 8) |
| #define PCI_ST_DEVSEL_TIM (3 << 9) /* 2 bits */ |
| #define PCI_ST_SIG_TAR_ABRT (1 << 11) |
| #define PCI_ST_REC_TAR_ABRT (1 << 12) |
| #define PCI_ST_REC_MAS_ABRT (1 << 13) |
| #define PCI_ST_SIG_SYS_ERR (1 << 14) |
| #define PCI_ST_PAR_ERR (1 << 15) |
| |
| /* BARS: Base Address Registers */ |
| #define PCI_BAR_IO 0x1 /* 1 == IO, 0 == Mem */ |
| #define PCI_BAR_IO_MASK 0xfffffffc |
| #define PCI_BAR_MEM_MASK 0xfffffff0 |
| #define PCI_MEMBAR_TYPE (3 << 1) |
| #define PCI_MEMBAR_32BIT 0x0 |
| #define PCI_MEMBAR_RESV 0x2 /* type 0x1 shifted to MEMBAR_TYPE */ |
| #define PCI_MEMBAR_64BIT 0x4 /* type 0x2 shifted to MEMBAR_TYPE */ |
| |
| #define PCI_MAX_BUS 256 |
| #define PCI_MAX_DEV 32 |
| #define PCI_MAX_FUNC 8 |
| |
| // Run the PCI Code to loop over the PCI BARs. For now we don't use the BARs, |
| // dont check em. |
| #define CHECK_BARS 0 |
| |
| #define MAX_PCI_BAR 6 |
| |
| /* Nothing yet, but this helps with Linux drivers. */ |
| struct device { |
| }; |
| |
| struct pci_bar { |
| uint32_t raw_bar; |
| uint32_t pio_base; |
| uint32_t mmio_base32; |
| uint64_t mmio_base64; |
| uint32_t mmio_sz; |
| }; |
| |
| struct pci_device { |
| STAILQ_ENTRY(pci_device) all_dev; /* list of all devices */ |
| SLIST_ENTRY(pci_device) irq_dev; /* list of all devs on irq */ |
| char name[9]; |
| spinlock_t lock; |
| void *dev_data; /* device private pointer */ |
| struct device device; |
| bool in_use; /* prevent double discovery */ |
| uint8_t bus; |
| uint8_t dev; |
| uint8_t func; |
| uint16_t dev_id; |
| uint16_t ven_id; |
| uint8_t irqline; |
| uint8_t irqpin; |
| char *header_type; |
| uint8_t class; |
| uint8_t subclass; |
| uint8_t progif; |
| bool msi_ready; |
| uint32_t msi_msg_addr_hi; |
| uint32_t msi_msg_addr_lo; |
| uint32_t msi_msg_data; |
| uint8_t nr_bars; |
| struct pci_bar bar[MAX_PCI_BAR]; |
| uint32_t caps[PCI_CAP_ID_MAX + 1]; |
| uintptr_t msix_tbl_paddr; |
| uintptr_t msix_tbl_vaddr; |
| uintptr_t msix_pba_paddr; |
| uintptr_t msix_pba_vaddr; |
| unsigned int msix_nr_vec; |
| bool msix_ready; |
| }; |
| |
| struct msix_entry { |
| uint32_t addr_lo, addr_hi, data, vector; |
| }; |
| |
| struct msix_irq_vector { |
| struct pci_device *pcidev; |
| struct msix_entry *entry; |
| uint32_t addr_lo; |
| uint32_t addr_hi; |
| uint32_t data; |
| }; |
| |
| /* List of all discovered devices */ |
| STAILQ_HEAD(pcidev_stailq, pci_device); |
| extern struct pcidev_stailq pci_devices; |
| |
| /* Sync rules for PCI: once a device is added to the list, it is never removed, |
| * and its read-only fields can be accessed at any time. There is no need for |
| * refcnts or things like that. |
| * |
| * The device list is built early on when we're single threaded, so I'm not |
| * bothering with locks for that yet. Append-only, singly-linked-list reads |
| * don't need a lock either. |
| * |
| * Other per-device accesses (like read-modify-writes to config space or MSI |
| * fields) require the device's lock. If we ever want to unplug, we'll probably |
| * work out an RCU-like scheme for the pci_devices list. |
| * |
| * Note this is in addition to the config space global locking done by every |
| * pci_read or write call. */ |
| |
| void pci_init(void); |
| void pcidev_print_info(struct pci_device *pcidev, int verbosity); |
| uint32_t pci_config_addr(uint8_t bus, uint8_t dev, uint8_t func, uint32_t reg); |
| |
| /* Read and write helpers (Eventually, we should have these be statics, since no |
| * device should touch PCI config space). */ |
| uint32_t pci_read32(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset); |
| void pci_write32(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset, |
| uint32_t value); |
| uint16_t pci_read16(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset); |
| void pci_write16(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset, |
| uint16_t value); |
| uint8_t pci_read8(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset); |
| void pci_write8(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset, |
| uint8_t value); |
| uint32_t pcidev_read32(struct pci_device *pcidev, uint32_t offset); |
| void pcidev_write32(struct pci_device *pcidev, uint32_t offset, uint32_t value); |
| uint16_t pcidev_read16(struct pci_device *pcidev, uint32_t offset); |
| void pcidev_write16(struct pci_device *pcidev, uint32_t offset, uint16_t value); |
| uint8_t pcidev_read8(struct pci_device *pcidev, uint32_t offset); |
| void pcidev_write8(struct pci_device *pcidev, uint32_t offset, uint8_t value); |
| |
| /* Other common PCI functions */ |
| void pci_set_bus_master(struct pci_device *pcidev); |
| void pci_clr_bus_master(struct pci_device *pcidev); |
| struct pci_device *pci_match_tbdf(int tbdf); |
| uintptr_t pci_get_membar(struct pci_device *pcidev, int bir); |
| uintptr_t pci_get_iobar(struct pci_device *pcidev, int bir); |
| uint32_t pci_get_membar_sz(struct pci_device *pcidev, int bir); |
| uint16_t pci_get_vendor(struct pci_device *pcidev); |
| uint16_t pci_get_device(struct pci_device *pcidev); |
| uint16_t pci_get_subvendor(struct pci_device *pcidev); |
| uint16_t pci_get_subdevice(struct pci_device *pcidev); |
| void pci_dump_config(struct pci_device *pcidev, size_t len); |
| int pci_find_cap(struct pci_device *pcidev, uint8_t cap_id, uint32_t *cap_reg); |
| unsigned int pci_to_tbdf(struct pci_device *pcidev); |
| uintptr_t pci_map_membar(struct pci_device *dev, int bir); |
| int pci_set_cacheline_size(struct pci_device *dev); |
| int pci_set_mwi(struct pci_device *dev); |
| void pci_clear_mwi(struct pci_device *dev); |
| static inline void pci_set_drvdata(struct pci_device *pcidev, void *data); |
| static inline void *pci_get_drvdata(struct pci_device *pcidev); |
| |
| /* MSI functions, msi.c */ |
| int pci_msi_enable(struct pci_device *p, uint64_t vec); |
| int pci_msix_init(struct pci_device *p); |
| struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec); |
| void pci_msi_mask(struct pci_device *p); |
| void pci_msi_unmask(struct pci_device *p); |
| void pci_msi_route(struct pci_device *p, int dest); |
| void pci_msix_mask_vector(struct msix_irq_vector *linkage); |
| void pci_msix_unmask_vector(struct msix_irq_vector *linkage); |
| void pci_msix_route_vector(struct msix_irq_vector *linkage, int dest); |
| |
| /* TODO: this is quite the Hacke */ |
| #define explode_tbdf(tbdf) {pcidev.bus = tbdf >> 16;\ |
| pcidev.dev = (tbdf>>11)&0x1f;\ |
| pcidev.func = (tbdf>>8)&3;} |
| |
| static inline void pci_set_drvdata(struct pci_device *pcidev, void *data) |
| { |
| pcidev->dev_data = data; |
| } |
| |
| static inline void *pci_get_drvdata(struct pci_device *pcidev) |
| { |
| return pcidev->dev_data; |
| } |