]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/io/server/src/pci_iomem_root_bridge.cc
abb5f95d42e919f05a6bb1121251e4c77ad16818
[l4.git] / l4 / pkg / io / server / src / pci_iomem_root_bridge.cc
1 /*
2  * (c) 2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
3  *          Alexander Warg <warg@os.inf.tu-dresden.de>
4  *     economic rights: Technische Universität Dresden (Germany)
5  *
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU General Public License 2.
8  * Please see the COPYING-GPL-2 file for details.
9  */
10 #include "hw_device.h"
11 #include "pci.h"
12 #include "main.h"
13
14 namespace {
15
16 inline
17 l4_uint32_t
18 pci_conf_addr0(l4_uint32_t bus, l4_uint32_t dev,
19                l4_uint32_t fn, l4_uint32_t reg)
20 { return (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3); }
21
22 class Pci_iomem_root_bridge : public Pci_root_bridge, public Hw::Device
23 {
24 public:
25   typedef Hw::Pci::Cfg_width Cfg_width;
26
27   Pci_iomem_root_bridge()
28   : Pci_root_bridge(this), _iobase_virt(0), _iobase_phys(~0UL),
29     _dev_start(~0UL), _dev_end(~0UL), _iosize(0)
30   {
31     set_discover_bus_if(this);
32   }
33
34   int cfg_read(unsigned bus, l4_uint32_t devfn, l4_uint32_t reg,
35                l4_uint32_t *value, Cfg_width);
36
37   int cfg_write(unsigned bus, l4_uint32_t devfn, l4_uint32_t reg,
38                 l4_uint32_t value, Cfg_width);
39
40   void scan_bus();
41
42   void init();
43
44   int set_property(cxx::String const &prop, Prop_val const &val);
45   template< typename T>
46   int getval(const char *s, cxx::String const &prop,
47              Prop_val const &val, T *rval);
48   int int_map(int i) const { return _int_map[i]; }
49
50 private:
51   l4_addr_t _iobase_virt, _iobase_phys, _dev_start, _dev_end;
52   int _int_map[4];
53   l4_size_t _iosize;
54 };
55
56 // Irq router that maps INTA-D to GSI
57 class Irq_router_rs : public Resource_space
58 {
59 public:
60   bool request(Resource *parent, Device *, Resource *child, Device *cdev);
61   bool alloc(Resource *, Device *, Resource *, Device *, bool)
62   { return false; }
63 };
64
65 void
66 Pci_iomem_root_bridge::init()
67 {
68   if (_iobase_phys == ~0UL)
69     {
70       printf("ERROR: Pci_root_bridge: 'iobase' not set.\n");
71       return;
72     }
73
74   if (_iosize == 0U)
75     {
76       printf("ERROR: Pci_root_bridge: 'iosize' not set.\n");
77       return;
78     }
79
80   if (_dev_start == ~0UL || _dev_end == ~0UL)
81     {
82       printf("ERROR: Pci_root_bridge: 'dev_start' and/or 'dev_end' not set.\n");
83       return;
84     }
85
86   _iobase_virt = res_map_iomem(_iobase_phys, _iosize);
87   if (!_iobase_virt)
88     return;
89
90   add_resource(new Adr_resource(Resource::Mmio_res
91                                 | Resource::F_fixed_size
92                                 | Resource::F_fixed_addr,
93                                 _iobase_phys,
94                                 _iobase_phys + _iosize - 1));
95
96   Adr_resource *r = new Adr_resource_provider(Resource::Mmio_res
97                                               | Resource::F_fixed_size
98                                               | Resource::F_fixed_addr);
99   r->alignment(0xfffff);
100   r->start_end(_dev_start, _dev_end);
101   add_resource(r);
102
103   r = new Adr_resource_provider(Resource::Io_res
104                                 | Resource::F_fixed_size
105                                 | Resource::F_fixed_addr);
106   r->start_end(0, 0xffff);
107   add_resource(r);
108
109   add_resource(new Pci_irq_router_res<Irq_router_rs>());
110
111   Hw::Device::init();
112 }
113
114 void
115 Pci_iomem_root_bridge::scan_bus()
116 {
117   if (!_iobase_virt)
118     return;
119   Pci_root_bridge::scan_bus();
120 }
121
122 int
123 Pci_iomem_root_bridge::cfg_read(unsigned bus, l4_uint32_t devfn,
124                                 l4_uint32_t reg,
125                                 l4_uint32_t *value, Cfg_width order)
126 {
127   using namespace Hw;
128
129   if (!_iobase_virt)
130     return -1;
131
132   l4_uint32_t a = _iobase_virt + pci_conf_addr0(bus, devfn >> 16, devfn & 0xffff, reg);
133   switch (order)
134     {
135     case Pci::Cfg_byte: *value = *(volatile unsigned char *)(a + (reg & 3)); break;
136     case Pci::Cfg_short: *value = *(volatile unsigned short *)(a + (reg & 2)); break;
137     case Pci::Cfg_long: *value = *(volatile unsigned int *)a; break;
138     }
139
140   if (0)
141     printf("Pci_iomem_root_bridge::cfg_read(%x, %x, %x, %x, %d)\n",
142            bus, devfn, reg, *value, order);
143
144   return 0;
145 }
146
147 int
148 Pci_iomem_root_bridge::cfg_write(unsigned bus, l4_uint32_t devfn,
149                                  l4_uint32_t reg,
150                                  l4_uint32_t value, Cfg_width order)
151 {
152   using namespace Hw;
153
154   if (!_iobase_virt)
155     return -1;
156
157   if (0)
158     printf("Pci_iomem_root_bridge::cfg_write(%x, %x, %x, %x, %d)\n",
159            bus, devfn, reg, value, order);
160
161   l4_uint32_t a = _iobase_virt + pci_conf_addr0(bus, devfn >> 16, devfn & 0xffff, reg);
162   switch (order)
163     {
164     case Pci::Cfg_byte: *(volatile unsigned char *)(a + (reg & 3)) = value; break;
165     case Pci::Cfg_short: *(volatile unsigned short *)(a + (reg & 2)) = value; break;
166     case Pci::Cfg_long: *(volatile unsigned int *)a = value; break;
167     }
168   return 0;
169 }
170
171 template< typename T>
172 int
173 Pci_iomem_root_bridge::getval(const char *s, cxx::String const &prop,
174                               Prop_val const &val, T *rval)
175 {
176   if (prop == s)
177     {
178       if (val.type != Prop_val::Int)
179         return -E_inval;
180
181       *rval = val.val.integer;
182       return E_ok;
183     }
184   return -E_no_prop;
185 }
186
187 int
188 Pci_iomem_root_bridge::set_property(cxx::String const &prop, Prop_val const &val)
189 {
190   int r;
191
192   if ((r = getval("iobase", prop, val, &_iobase_phys)) != -E_no_prop)
193     return r;
194   else if ((r = getval("iosize", prop, val, &_iosize)) != -E_no_prop)
195     return r;
196   else if ((r = getval("dev_start", prop, val, &_dev_start)) != -E_no_prop)
197     return r;
198   else if ((r = getval("dev_end", prop, val, &_dev_end)) != -E_no_prop)
199     return r;
200   else if ((r = getval("int_a", prop, val, &_int_map[0])) != -E_no_prop)
201     return r;
202   else if ((r = getval("int_b", prop, val, &_int_map[1])) != -E_no_prop)
203     return r;
204   else if ((r = getval("int_c", prop, val, &_int_map[2])) != -E_no_prop)
205     return r;
206   else if ((r = getval("int_d", prop, val, &_int_map[3])) != -E_no_prop)
207     return r;
208
209   return Hw::Device::set_property(prop, val);
210 }
211
212 static Hw::Device_factory_t<Pci_iomem_root_bridge>
213               __hw_pci_root_bridge_factory("Pci_iomem_root_bridge");
214
215 bool Irq_router_rs::request(Resource *parent, Device *pdev,
216                             Resource *child, Device *cdev)
217 {
218   Adr_resource *cr = dynamic_cast<Adr_resource*>(child);
219
220   if (!cr)
221     {
222       child->parent(parent);
223       return true;
224     }
225
226   Hw::Device *cd = dynamic_cast<Hw::Device*>(cdev);
227   if (!cd)
228     return false;
229
230   if (cr->start() > 3)
231     return false;
232
233   int i = (cr->start() + (cd->adr() >> 16)) & 3;
234
235   Pci_iomem_root_bridge *pd = dynamic_cast<Pci_iomem_root_bridge *>(pdev);
236   if (!pd)
237     return false;
238
239
240   cr->del_flags(Resource::F_relative);
241   cr->start(pd->int_map(i));
242   cr->del_flags(Resource::Irq_info_base * 3);
243   cr->add_flags(Resource::Irq_level | Resource::Irq_low);
244
245   cr->parent(parent);
246
247   return true;
248 }
249 }