Contiki 3.x
pci.c
1 /*
2  * Copyright (C) 2015, Intel Corporation. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  *
13  * 3. Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <assert.h>
32 
33 #include "pci.h"
34 #include "helpers.h"
35 #include "syscalls.h"
36 
37 /* I/O port for PCI configuration address */
38 #define PCI_CONFIG_ADDR_PORT 0xCF8
39 /* I/O port for PCI configuration data */
40 #define PCI_CONFIG_DATA_PORT 0xCFC
41 
42 PROT_DOMAINS_ALLOC(dom_client_data_t, root_complex_drv);
43 
44 /*---------------------------------------------------------------------------*/
45 /* Initialize PCI configuration register address in preparation for accessing
46  * the specified register.
47  */
48 static void
49 set_addr(pci_config_addr_t addr)
50 {
51  addr.en_mapping = 1;
52 
53  outl(PCI_CONFIG_ADDR_PORT, addr.raw);
54 }
55 /*---------------------------------------------------------------------------*/
56 /**
57  * \brief Read from the specified PCI configuration register.
58  * \param addr Address of PCI configuration register.
59  * \return Value read from PCI configuration register.
60  */
61 uint32_t
62 pci_config_read(pci_config_addr_t addr)
63 {
64  set_addr(addr);
65 
66  return inl(PCI_CONFIG_DATA_PORT);
67 }
68 /*---------------------------------------------------------------------------*/
69 /**
70  * \brief Write to the PCI configuration data port.
71  * \param addr Address of PCI configuration register.
72  * \param data Value to write.
73  */
74 void
75 pci_config_write(pci_config_addr_t addr, uint32_t data)
76 {
77  set_addr(addr);
78 
79  outl(PCI_CONFIG_DATA_PORT, data);
80 }
81 /*---------------------------------------------------------------------------*/
82 /**
83  * \brief Enable PCI command bits of the specified PCI configuration
84  * register.
85  * \param addr Address of PCI configuration register.
86  * \param flags Flags used to enable PCI command bits.
87  */
88 void
89 pci_command_enable(pci_config_addr_t addr, uint32_t flags)
90 {
91  uint32_t data;
92 
93  addr.reg_off = 0x04; /* PCI COMMAND_REGISTER */
94 
95  data = pci_config_read(addr);
96  pci_config_write(addr, data | flags);
97 }
98 /*---------------------------------------------------------------------------*/
99 /**
100  * \brief Set current PIRQ to interrupt queue agent. PCI based interrupts
101  * PIRQ[A:H] are then available for consumption by either the 8259
102  * PICs or the IO-APIC depending on configuration of the 8 PIRQx
103  * Routing Control Registers PIRQ[A:H]. See also pci_pirq_set_irq().
104  * \param agent Interrupt Queue Agent to be used, IRQAGENT[0:3].
105  * \param pin Interrupt Pin Route to be used, INT[A:D].
106  * \param pirq PIRQ to be used, PIRQ[A:H].
107  */
108 SYSCALLS_DEFINE_SINGLETON(pci_irq_agent_set_pirq,
109  root_complex_drv,
110  IRQAGENT agent, INTR_PIN pin, PIRQ pirq)
111 {
112  uint16_t value;
113  uint32_t rcba_addr, offset = 0;
114 
115  rcba_addr = PROT_DOMAINS_MMIO(root_complex_drv);
116 
117  assert(agent >= IRQAGENT0 && agent <= IRQAGENT3);
118  assert(pin >= INTA && pin <= INTD);
119  assert(pirq >= PIRQA && pirq <= PIRQH);
120 
121  switch(agent) {
122  case IRQAGENT0:
123  if(pin != INTA) {
124  halt();
125  }
126  offset = 0x3140;
127  break;
128  case IRQAGENT1:
129  offset = 0x3142;
130  break;
131  case IRQAGENT2:
132  if(pin != INTA) {
133  halt();
134  }
135  offset = 0x3144;
136  break;
137  case IRQAGENT3:
138  offset = 0x3146;
139  }
140 
141  prot_domains_enable_mmio();
142 
143  MMIO_READW(value, *(uint16_t ATTR_MMIO_ADDR_SPACE *)(rcba_addr + offset));
144 
145  /* clear interrupt pin route and set corresponding pirq. */
146  switch(pin) {
147  case INTA:
148  value &= ~0xF;
149  value |= pirq;
150  break;
151  case INTB:
152  value &= ~0xF0;
153  value |= (pirq << 4);
154  break;
155  case INTC:
156  value &= ~0xF00;
157  value |= (pirq << 8);
158  break;
159  case INTD:
160  value &= ~0xF000;
161  value |= (pirq << 12);
162  }
163 
164  MMIO_WRITEW(*(uint16_t ATTR_MMIO_ADDR_SPACE *)(rcba_addr + offset), value);
165 
166  prot_domains_disable_mmio();
167 }
168 /*---------------------------------------------------------------------------*/
169 /**
170  * \brief Set current IRQ to PIRQ. The interrupt router can be
171  * programmed to allow PIRQ[A:H] to be routed internally
172  * to the 8259 as ISA compatible interrupts. See also
173  * pci_irq_agent_set_pirq().
174  * \param pirq PIRQ to be used, PIRQ[A:H].
175  * \param pin IRQ to be used, IRQ[0:15].
176  * \param route_to_legacy Whether or not the interrupt should be routed to PIC 8259.
177  */
178 void
179 pci_pirq_set_irq(PIRQ pirq, uint8_t irq, uint8_t route_to_legacy)
180 {
181  pci_config_addr_t pci;
182  uint32_t value;
183 
184  assert(pirq >= PIRQA && pirq <= PIRQH);
185  assert(irq >= 0 && irq <= 0xF);
186  assert(route_to_legacy == 0 || route_to_legacy == 1);
187 
188  pci.raw = 0;
189  pci.bus = 0;
190  pci.dev = 31;
191  pci.func = 0;
192  pci.reg_off = (pirq <= PIRQD) ? 0x60 : 0x64; /* PABCDRC and PEFGHRC Registers */
193 
194  value = pci_config_read(pci);
195 
196  switch(pirq) {
197  case PIRQA:
198  case PIRQE:
199  value &= ~0x8F;
200  value |= irq;
201  value |= (!route_to_legacy << 7);
202  break;
203  case PIRQB:
204  case PIRQF:
205  value &= ~0x8F00;
206  value |= (irq << 8);
207  value |= (!route_to_legacy << 15);
208  break;
209  case PIRQC:
210  case PIRQG:
211  value &= ~0x8F0000;
212  value |= (irq << 16);
213  value |= (!route_to_legacy << 23);
214  break;
215  case PIRQD:
216  case PIRQH:
217  value &= ~0x8F000000;
218  value |= (irq << 24);
219  value |= (!route_to_legacy << 31);
220  }
221 
222  set_addr(pci);
223  outl(PCI_CONFIG_DATA_PORT, value);
224 }
225 /*---------------------------------------------------------------------------*/
226 /**
227  * \brief Initialize a structure for a PCI device driver that performs
228  * MMIO to address range 0. Assumes that device has already
229  * been configured with an MMIO address range 0, e.g. by
230  * firmware.
231  * \param c_this Structure that will be initialized to represent the driver.
232  * \param pci_addr PCI base address of device.
233  * \param mmio_sz Size of MMIO region.
234  * \param meta Base address of optional driver-defined metadata.
235  * \param meta_sz Size of optional driver-defined metadata.
236  */
237 void
238 pci_init(pci_driver_t ATTR_KERN_ADDR_SPACE *c_this,
239  pci_config_addr_t pci_addr,
240  size_t mmio_sz,
241  uintptr_t meta,
242  size_t meta_sz)
243 {
244  uintptr_t mmio;
245 
246  /* The BAR value is masked to clear non-address bits. */
247  mmio = pci_config_read(pci_addr) & ~0xFFF;
248 
249  prot_domains_reg(c_this, mmio, mmio_sz, meta, meta_sz, false);
250 }
251 /*---------------------------------------------------------------------------*/
252 /**
253  * \brief Initialize the PCI root complex driver.
254  */
255 void
256 pci_root_complex_init(void)
257 {
258  uint32_t rcba_addr;
259  pci_config_addr_t pci = { .raw = 0 };
260  pci.dev = 31;
261  pci.reg_off = 0xF0; /* Root Complex Base Address Register */
262 
263  /* masked to clear non-address bits. */
264  rcba_addr = pci_config_read(pci) & ~0x3FFF;
265 
266  PROT_DOMAINS_INIT_ID(root_complex_drv);
267  prot_domains_reg(&root_complex_drv, rcba_addr, 0x4000, 0, 0, false);
268  SYSCALLS_INIT(pci_irq_agent_set_pirq);
269  SYSCALLS_AUTHZ(pci_irq_agent_set_pirq, root_complex_drv);
270 }
271 /*---------------------------------------------------------------------------*/
272 /**
273  * \brief Prevent further invocations of pci_irq_agent_set_pirq.
274  */
275 void
276 pci_root_complex_lock(void)
277 {
278  SYSCALLS_DEAUTHZ(pci_irq_agent_set_pirq, root_complex_drv);
279 }
280 /*---------------------------------------------------------------------------*/
uint32_t func
Function number.
Definition: pci.h:89
static uip_ds6_addr_t * addr
Pointer to a router list entry.
Definition: uip-nd6.c:124
PCI configuration address.
Definition: pci.h:85
uint32_t dev
Device number.
Definition: pci.h:90
uint32_t bus
Bus number.
Definition: pci.h:91
Data associated with each protection domain that is owned by clients of that domain and used to ident...
Definition: prot-domains.h:247
uint32_t en_mapping
Must be set to perform PCI configuration access.
Definition: pci.h:94
uint32_t reg_off
Register/offset number.
Definition: pci.h:88