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