]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/hpet.cpp
update
[l4.git] / kernel / fiasco / src / kern / hpet.cpp
1 INTERFACE:
2
3 #include "acpi.h"
4 #include "div32.h"
5 #include "mem.h"
6
7 class Acpi_hpet : public Acpi_table_head
8 {
9 public:
10   Unsigned32 event_timer_block_id;
11   Acpi_gas   base_address;
12   Unsigned8  hpet_number;
13   Unsigned16 min_clock_ticks_for_irq;
14   Unsigned8  page_prot_and_oem_attr;
15 } __attribute__((packed));
16
17 class Hpet_timer
18 {
19 public:
20   Unsigned64 config_and_cap;
21   Unsigned64 comp_val;
22   Unsigned64 irq_route;
23   Unsigned64 _pad0;
24
25   enum
26   {
27     Tn_INT_TYPE_CNF       = 1 << 1,
28     Tn_INT_ENB_CNF        = 1 << 2,
29     Tn_TYPE_CNF           = 1 << 3,
30     Tn_PER_INT_CAP        = 1 << 4,
31     Tn_SIZE_CAP           = 1 << 5,
32     Tn_VAL_SET_CNF        = 1 << 6,
33     Tn_32MODE_CNF         = 1 << 8,
34     Tn_FSB_EN_CNF         = 1 << 14,
35     Tn_FSB_INT_DEL_CAP    = 1 << 15,
36     Tn_INT_ROUTE_CNF_MASK = 31 << 9,
37   };
38
39   void enable_int()  { config_and_cap |=  Tn_INT_ENB_CNF; }
40   void disable_int() { config_and_cap &= ~Tn_INT_ENB_CNF; }
41   void set_int(int irq)
42   { config_and_cap = (config_and_cap & ~Tn_INT_ROUTE_CNF_MASK) | (irq << 9); }
43
44   int get_int() const { return (config_and_cap & Tn_INT_ROUTE_CNF_MASK) >> 9; }
45
46   bool can_periodic() const { return config_and_cap & Tn_PER_INT_CAP; }
47   void set_periodic()       { config_and_cap |= Tn_TYPE_CNF; }
48
49   void set_level_irq() { config_and_cap |=  Tn_INT_TYPE_CNF; }
50   void set_edge_irq()  { config_and_cap &= ~Tn_INT_TYPE_CNF; }
51
52   bool can_64bit() const { return config_and_cap & Tn_SIZE_CAP; }
53   void force_32bit()     { config_and_cap |= Tn_32MODE_CNF; }
54   void val_set()         { config_and_cap |= Tn_VAL_SET_CNF; }
55
56   Unsigned32 int_route_cap() const { return config_and_cap >> 32; }
57   bool       int_avail(int i) const { return int_route_cap() & i; }
58
59   int get_first_int()
60   {
61     Unsigned32 cap = int_route_cap();
62     for (int i = 0; i < 32; ++i)
63       if (cap & (1 << i))
64         return i;
65     return ~0U;
66   }
67 } __attribute__((packed));
68
69 class Hpet_device
70 {
71 private:
72 public:
73   Unsigned64 cap_and_id;        // offset 0x00
74   Unsigned64 _pad0;
75   Unsigned64 config;            // offset 0x10
76   Unsigned64 _pad1;
77   Unsigned64 irq_status;        // offset 0x20
78   Unsigned64 _pad2[1 + 2 * 12];
79   Unsigned64 counter_val;       // 0ffset 0xf0
80
81   enum Config
82   {
83     ENABLE_CNF = 1 << 0,
84     LEG_RT_CNF = 1 << 1,
85   };
86
87 public:
88
89   int num_timers() const { return ((cap_and_id >> 8) & 0xf) + 1; }
90
91   Hpet_timer *timer(int idx) const
92   { return reinterpret_cast<Hpet_timer *>((char *)this + 0x100 + idx * 0x20); }
93
94   void enable()  { config |= ENABLE_CNF; Mem::mb(); }
95   void disable() { config &= ~ENABLE_CNF; Mem::mb(); }
96
97   Unsigned32 counter_clk_period() const { return cap_and_id >> 32; }
98   void reset_counter() { counter_val = 0; Mem::mb(); }
99
100   void clear_level_irq(int timer_nr)
101   { irq_status = 1 << timer_nr; Mem::mb(); }
102
103 } __attribute__((packed));
104
105 class Hpet
106 {
107 public:
108   static void set_periodic()   { _hpet_timer->set_periodic(); }
109   static void enable_timer()   { _hpet_timer->enable_int(); }
110   static void disable_timer()  { _hpet_timer->disable_int(); }
111   static void clear_timer()    { /* _hpet->clear_level_irq(2); */ }
112   static int  int_num()        { return _hpet_timer->get_int(); }
113   static bool int_avail(int i) { return _hpet_timer->int_avail(i); }
114
115   static void enable()         { _hpet->enable(); }
116   static void dump() { _hpet->dump(); }
117
118 private:
119   static Acpi_hpet const *_acpi_hpet;
120   static Hpet_device *_hpet;
121   static Hpet_timer *_hpet_timer;
122 };
123
124 IMPLEMENTATION:
125
126 Acpi_hpet const *Hpet::_acpi_hpet;
127 Hpet_device *Hpet::_hpet;
128 Hpet_timer *Hpet::_hpet_timer;
129
130 PUBLIC
131 void
132 Hpet_device::dump()
133 {
134   printf("HPET: cap+id : %016llx\n", cap_and_id);
135   printf("      clk-per: %d femtosecs\n", counter_clk_period());
136   printf("      gen-config: %llx\n", config);
137 }
138
139 PUBLIC static
140 void
141 Hpet::dump_acpi_infos()
142 {
143   if (!_acpi_hpet)
144     return;
145
146   printf("ACPI-HPET = %p\n", _acpi_hpet);
147
148   printf("  event_timer_block_id:    %x\n", _acpi_hpet->event_timer_block_id);
149   printf("  base_address:            %llx (as: %d, off: %d, w: %d, id: %d)\n",
150          _acpi_hpet->base_address.addr,
151          _acpi_hpet->base_address.access_size,
152          _acpi_hpet->base_address.offset,
153          _acpi_hpet->base_address.width,
154          _acpi_hpet->base_address.id);
155   printf("  hpet_number:             %d\n", _acpi_hpet->hpet_number);
156   printf("  min_clock_ticks_for_irq: %d\n", _acpi_hpet->min_clock_ticks_for_irq);
157   printf("  page_prot_and_oem_attr:  %x\n", _acpi_hpet->page_prot_and_oem_attr);
158 }
159
160 PUBLIC static FIASCO_INIT
161 bool
162 Hpet::init()
163 {
164   _acpi_hpet = Acpi::find<Acpi_hpet const *>("HPET");
165
166   if (!_acpi_hpet)
167     {
168       printf("Could not find HPET in RSDT nor XSDT, skipping init\n");
169       return false;
170     }
171
172   dump_acpi_infos();
173
174   Address offs;
175   Address a = _acpi_hpet->base_address.addr;
176   Address va = Mem_layout::alloc_io_vmem(Config::PAGE_SIZE);
177   assert (va);
178
179   Kmem::map_phys_page(a, va, false, true, &offs);
180
181   Kip::k()->add_mem_region(Mem_desc(a, a + 1023, Mem_desc::Reserved));
182
183   _hpet = (Hpet_device *)(va + offs);
184
185   _hpet->dump();
186
187   _hpet->disable();
188   _hpet->reset_counter();
189
190   int i = 2;
191   Hpet_timer *t = 0;
192   for (; i < _hpet->num_timers(); ++i)
193     {
194       t = _hpet->timer(i);
195
196       if (t->can_periodic() && t->int_route_cap())
197         {
198           t->force_32bit();
199           t->set_periodic();
200           t->set_edge_irq();
201           t->val_set();
202           t->set_int(t->get_first_int());
203
204           t->comp_val = div32(1000000000000ULL, _hpet->counter_clk_period());
205
206           break;
207         }
208     }
209
210   if (!t)
211     {
212       printf("ERROR: Did not find a HPET timer that can do periodic mode.\n");
213       return false;
214     }
215
216   _hpet_timer = t;
217
218   _hpet->dump();
219
220   return true;
221 }
222
223 // ------------------------------------------------------------------------
224 IMPLEMENTATION [debug]:
225
226 PUBLIC static
227 Hpet_device *
228 Hpet::hpet()
229 { return _hpet; }