iPXE
pcimsix.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <stdint.h>
27 #include <errno.h>
28 #include <assert.h>
29 #include <ipxe/pci.h>
30 #include <ipxe/pcimsix.h>
31 
32 /** @file
33  *
34  * PCI MSI-X interrupts
35  *
36  */
37 
38 /**
39  * Get MSI-X descriptor name (for debugging)
40  *
41  * @v cfg Configuration space offset
42  * @ret name Descriptor name
43  */
44 static const char * pci_msix_name ( unsigned int cfg ) {
45 
46  switch ( cfg ) {
47  case PCI_MSIX_DESC_TABLE: return "table";
48  case PCI_MSIX_DESC_PBA: return "PBA";
49  default: return "<UNKNOWN>";
50  }
51 }
52 
53 /**
54  * Map MSI-X BAR portion
55  *
56  * @v pci PCI device
57  * @v msix MSI-X capability
58  * @v cfg Configuration space offset
59  * @ret io I/O address
60  */
61 static void * pci_msix_ioremap ( struct pci_device *pci, struct pci_msix *msix,
62  unsigned int cfg ) {
63  uint32_t desc;
64  unsigned int bar;
65  unsigned long start;
66  unsigned long offset;
67  unsigned long base;
68  void *io;
69 
70  /* Read descriptor */
71  pci_read_config_dword ( pci, ( msix->cap + cfg ), &desc );
72 
73  /* Get BAR */
74  bar = PCI_MSIX_DESC_BIR ( desc );
75  offset = PCI_MSIX_DESC_OFFSET ( desc );
76  start = pci_bar_start ( pci, PCI_BASE_ADDRESS ( bar ) );
77  if ( ! start ) {
78  DBGC ( msix, "MSI-X %p %s could not find BAR%d\n",
79  msix, pci_msix_name ( cfg ), bar );
80  return NULL;
81  }
82  base = ( start + offset );
83  DBGC ( msix, "MSI-X %p %s at %#08lx (BAR%d+%#lx)\n",
84  msix, pci_msix_name ( cfg ), base, bar, offset );
85 
86  /* Map BAR portion */
87  io = pci_ioremap ( pci, ( start + offset ), PCI_MSIX_LEN );
88  if ( ! io ) {
89  DBGC ( msix, "MSI-X %p %s could not map %#08lx\n",
90  msix, pci_msix_name ( cfg ), base );
91  return NULL;
92  }
93 
94  return io;
95 }
96 
97 /**
98  * Enable MSI-X interrupts
99  *
100  * @v pci PCI device
101  * @v msix MSI-X capability
102  * @ret rc Return status code
103  */
104 int pci_msix_enable ( struct pci_device *pci, struct pci_msix *msix ) {
105  uint16_t ctrl;
106  int rc;
107 
108  /* Locate capability */
109  msix->cap = pci_find_capability ( pci, PCI_CAP_ID_MSIX );
110  if ( ! msix->cap ) {
111  DBGC ( msix, "MSI-X %p found no MSI-X capability in "
112  PCI_FMT "\n", msix, PCI_ARGS ( pci ) );
113  rc = -ENOENT;
114  goto err_cap;
115  }
116 
117  /* Extract interrupt count */
118  pci_read_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), &ctrl );
119  msix->count = ( PCI_MSIX_CTRL_SIZE ( ctrl ) + 1 );
120  DBGC ( msix, "MSI-X %p has %d vectors for " PCI_FMT "\n",
121  msix, msix->count, PCI_ARGS ( pci ) );
122 
123  /* Map MSI-X table */
124  msix->table = pci_msix_ioremap ( pci, msix, PCI_MSIX_DESC_TABLE );
125  if ( ! msix->table ) {
126  rc = -ENOENT;
127  goto err_table;
128  }
129 
130  /* Map pending bit array */
131  msix->pba = pci_msix_ioremap ( pci, msix, PCI_MSIX_DESC_PBA );
132  if ( ! msix->pba ) {
133  rc = -ENOENT;
134  goto err_pba;
135  }
136 
137  /* Enable MSI-X */
140  pci_write_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), ctrl );
141 
142  return 0;
143 
144  iounmap ( msix->pba );
145  err_pba:
146  iounmap ( msix->table );
147  err_table:
148  err_cap:
149  return rc;
150 }
151 
152 /**
153  * Disable MSI-X interrupts
154  *
155  * @v pci PCI device
156  * @v msix MSI-X capability
157  */
158 void pci_msix_disable ( struct pci_device *pci, struct pci_msix *msix ) {
159  uint16_t ctrl;
160 
161  /* Disable MSI-X */
162  pci_read_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), &ctrl );
164  pci_write_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), ctrl );
165 
166  /* Unmap pending bit array */
167  iounmap ( msix->pba );
168 
169  /* Unmap MSI-X table */
170  iounmap ( msix->table );
171 }
172 
173 /**
174  * Map MSI-X interrupt vector
175  *
176  * @v msix MSI-X capability
177  * @v vector MSI-X vector
178  * @v address Message address
179  * @v data Message data
180  */
181 void pci_msix_map ( struct pci_msix *msix, unsigned int vector,
183  void *base;
184 
185  /* Sanity check */
186  assert ( vector < msix->count );
187 
188  /* Map interrupt vector */
189  base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
190  writel ( ( address & 0xffffffffUL ), ( base + PCI_MSIX_ADDRESS_LO ) );
191  if ( sizeof ( address ) > sizeof ( uint32_t ) ) {
192  writel ( ( ( ( uint64_t ) address ) >> 32 ),
193  ( base + PCI_MSIX_ADDRESS_HI ) );
194  } else {
195  writel ( 0, ( base + PCI_MSIX_ADDRESS_HI ) );
196  }
197  writel ( data, ( base + PCI_MSIX_DATA ) );
198 }
199 
200 /**
201  * Control MSI-X interrupt vector
202  *
203  * @v msix MSI-X capability
204  * @v vector MSI-X vector
205  * @v mask Control mask
206  */
207 void pci_msix_control ( struct pci_msix *msix, unsigned int vector,
208  uint32_t mask ) {
209  void *base;
210  uint32_t ctrl;
211 
212  /* Mask/unmask interrupt vector */
213  base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
216  ctrl |= mask;
217  writel ( ctrl, ( base + PCI_MSIX_CONTROL ) );
218 }
219 
220 /**
221  * Dump MSI-X interrupt state (for debugging)
222  *
223  * @v msix MSI-X capability
224  * @v vector MSI-X vector
225  */
226 void pci_msix_dump ( struct pci_msix *msix, unsigned int vector ) {
227  void *base;
228  uint32_t address_hi;
229  uint32_t address_lo;
231  uint32_t data;
232  uint32_t ctrl;
233  uint32_t pba;
234 
235  /* Do nothing in non-debug builds */
236  if ( ! DBG_LOG )
237  return;
238 
239  /* Mask/unmask interrupt vector */
240  base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
241  address_hi = readl ( base + PCI_MSIX_ADDRESS_HI );
242  address_lo = readl ( base + PCI_MSIX_ADDRESS_LO );
243  data = readl ( base + PCI_MSIX_DATA );
245  pba = readl ( msix->pba );
246  address = ( ( ( ( uint64_t ) address_hi ) << 32 ) | address_lo );
247  DBGC ( msix, "MSI-X %p vector %d %#08x => %#08lx%s%s\n",
248  msix, vector, data, address,
249  ( ( ctrl & PCI_MSIX_CONTROL_MASK ) ? " (masked)" : "" ),
250  ( ( pba & ( 1 << vector ) ) ? " (pending)" : "" ) );
251 }
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
void pci_msix_disable(struct pci_device *pci, struct pci_msix *msix)
Disable MSI-X interrupts.
Definition: pcimsix.c:158
unsigned short uint16_t
Definition: stdint.h:11
int pci_find_capability(struct pci_device *pci, int cap)
Look for a PCI capability.
Definition: pciextra.c:36
Error codes.
uint32_t vector
MSI-X vector.
Definition: ena.h:20
uint64_t address
Base address.
Definition: ena.h:24
int pci_write_config_word(struct pci_device *pci, unsigned int where, uint16_t value)
Write 16-bit word to PCI configuration space.
uint32_t readl(volatile uint32_t *io_addr)
Read 32-bit dword from memory-mapped device.
#define DBGC(...)
Definition: compiler.h:505
#define ENOENT
No such file or directory.
Definition: errno.h:514
unsigned long long uint64_t
Definition: stdint.h:13
PCI MSI-X interrupts.
#define PCI_MSIX_CONTROL
MSI-X vector control.
Definition: pcimsix.h:30
int pci_read_config_word(struct pci_device *pci, unsigned int where, uint16_t *value)
Read 16-bit word from PCI configuration space.
#define PCI_MSIX_DESC_BIR(x)
BAR index.
Definition: pci.h:121
#define PCI_BASE_ADDRESS(n)
PCI base address registers.
Definition: pci.h:61
uint32_t start
Starting offset.
Definition: netvsc.h:12
Assertions.
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
int pci_read_config_dword(struct pci_device *pci, unsigned int where, uint32_t *value)
Read 32-bit dword from PCI configuration space.
void pci_msix_control(struct pci_msix *msix, unsigned int vector, uint32_t mask)
Control MSI-X interrupt vector.
Definition: pcimsix.c:207
PCI MSI-X capability.
Definition: pcimsix.h:34
void writel(uint32_t data, volatile uint32_t *io_addr)
Write 32-bit dword to memory-mapped device.
#define PCI_MSIX_CTRL
MSI-X interrupts.
Definition: pci.h:115
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
unsigned long pci_bar_start(struct pci_device *pci, unsigned int reg)
Find the start of a PCI BAR.
Definition: pci.c:96
unsigned int cap
Capability offset.
Definition: pcimsix.h:36
#define PCI_MSIX_VECTOR(n)
MSI-X vector offset.
Definition: pcimsix.h:18
#define PCI_MSIX_CTRL_SIZE(x)
Table size.
Definition: pci.h:118
#define PCI_MSIX_CTRL_MASK
Mask all interrupts.
Definition: pci.h:117
#define PCI_FMT
PCI device debug message format.
Definition: pci.h:290
PCI bus.
A PCI device.
Definition: pci.h:188
void * table
MSI-X table.
Definition: pcimsix.h:40
unsigned int uint32_t
Definition: stdint.h:12
uint16_t base
Base address.
Definition: edd.h:14
#define PCI_MSIX_LEN
MSI-X BAR mapped length.
Definition: pcimsix.h:15
#define PCI_MSIX_ADDRESS_LO
MSI-X vector address low 32 bits.
Definition: pcimsix.h:21
unsigned long physaddr_t
Definition: stdint.h:20
Definition: sis900.h:23
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
static void * pci_msix_ioremap(struct pci_device *pci, struct pci_msix *msix, unsigned int cfg)
Map MSI-X BAR portion.
Definition: pcimsix.c:61
void pci_msix_dump(struct pci_msix *msix, unsigned int vector)
Dump MSI-X interrupt state (for debugging)
Definition: pcimsix.c:226
#define PCI_CAP_ID_MSIX
MSI-X.
Definition: pci.h:98
#define PCI_MSIX_DATA
MSI-X vector data.
Definition: pcimsix.h:27
int pci_msix_enable(struct pci_device *pci, struct pci_msix *msix)
Enable MSI-X interrupts.
Definition: pcimsix.c:104
#define PCI_MSIX_DESC_OFFSET(x)
BAR offset.
Definition: pci.h:122
#define PCI_MSIX_CONTROL_MASK
Vector is masked.
Definition: pcimsix.h:31
uint16_t count
Number of entries.
Definition: ena.h:22
#define PCI_ARGS(pci)
PCI device debug message arguments.
Definition: pci.h:293
u8 ctrl
Definition: sky2.h:10
void iounmap(volatile const void *io_addr)
Unmap I/O address.
#define PCI_MSIX_CTRL_ENABLE
Enable MSI-X.
Definition: pci.h:116
struct arbelprm_port_state_change_st data
Message.
Definition: arbel.h:12
void pci_msix_map(struct pci_msix *msix, unsigned int vector, physaddr_t address, uint32_t data)
Map MSI-X interrupt vector.
Definition: pcimsix.c:181
void * pci_ioremap(struct pci_device *pci, unsigned long bus_addr, size_t len)
Map PCI bus address as an I/O address.
#define DBG_LOG
Definition: compiler.h:317
static const char * pci_msix_name(unsigned int cfg)
Get MSI-X descriptor name (for debugging)
Definition: pcimsix.c:44
#define PCI_MSIX_ADDRESS_HI
MSI-X vector address high 32 bits.
Definition: pcimsix.h:24
#define PCI_MSIX_DESC_PBA
Definition: pci.h:120
#define NULL
NULL pointer (VOID *)
Definition: Base.h:362
void * pba
Pending bit array.
Definition: pcimsix.h:42
unsigned int count
Number of vectors.
Definition: pcimsix.h:38
#define PCI_MSIX_DESC_TABLE
Definition: pci.h:119