]> 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
68 } __attribute__((packed));
69
70 class Hpet_device
71 {
72 private:
73 public:
74   Unsigned64 cap_and_id;        // offset 0x00
75   Unsigned64 _pad0;
76   Unsigned64 config;            // offset 0x10
77   Unsigned64 _pad1;
78   Unsigned64 irq_status;        // offset 0x20
79   Unsigned64 _pad2[1 + 2 * 12];
80   Unsigned64 counter_val;       // 0ffset 0xf0
81
82   enum Config
83   {
84     ENABLE_CNF = 1 << 0,
85     LEG_RT_CNF = 1 << 1,
86   };
87
88 public:
89
90   int num_timers() const { return ((cap_and_id >> 8) & 0xf) + 1; }
91
92   Hpet_timer *timer(int idx) const
93   { return reinterpret_cast<Hpet_timer *>((char *)this + 0x100 + idx * 0x20); }
94
95   void enable()  { config |= ENABLE_CNF; Mem::mb(); }
96   void disable() { config &= ~ENABLE_CNF; Mem::mb(); }
97
98   Unsigned32 counter_clk_period() const { return cap_and_id >> 32; }
99   void reset_counter() { counter_val = 0; Mem::mb(); }
100
101   void clear_level_irq(int timer_nr)
102   { irq_status = 1 << timer_nr; Mem::mb(); }
103
104 } __attribute__((packed));
105
106 class Hpet
107 {
108 public:
109   static void set_periodic()   { _hpet_timer->set_periodic(); }
110   static void enable_timer()   { _hpet_timer->enable_int(); }
111   static void disable_timer()  { _hpet_timer->disable_int(); }
112   static void clear_timer()    { /* _hpet->clear_level_irq(2); */ }
113   static int  int_num()        { return _hpet_timer->get_int(); }
114   static bool int_avail(int i) { return _hpet_timer->int_avail(i); }
115
116   static void enable()         { _hpet->enable(); }
117   static void dump() { _hpet->dump(); }
118
119 private:
120   static Acpi_hpet const *_acpi_hpet;
121   static Hpet_device *_hpet;
122   static Hpet_timer *_hpet_timer;
123 };
124
125 IMPLEMENTATION:
126
127 Acpi_hpet const *Hpet::_acpi_hpet;
128 Hpet_device *Hpet::_hpet;
129 Hpet_timer *Hpet::_hpet_timer;
130
131 PUBLIC
132 void
133 Hpet_device::dump()
134 {
135   printf("HPET: cap+id : %016llx\n", cap_and_id);
136   printf("      clk-per: %d femtosecs\n", counter_clk_period());
137   printf("      gen-config: %llx\n", config);
138 }
139
140 PUBLIC static
141 void
142 Hpet::dump_acpi_infos()
143 {
144   if (!_acpi_hpet)
145     return;
146
147   printf("ACPI-HPET = %p\n", _acpi_hpet);
148
149   printf("  event_timer_block_id:    %x\n", _acpi_hpet->event_timer_block_id);
150   printf("  base_address:            %llx (as: %d, off: %d, w: %d, id: %d)\n",
151          _acpi_hpet->base_address.addr,
152          _acpi_hpet->base_address.access_size,
153          _acpi_hpet->base_address.offset,
154          _acpi_hpet->base_address.width,
155          _acpi_hpet->base_address.id);
156   printf("  hpet_number:             %d\n", _acpi_hpet->hpet_number);
157   printf("  min_clock_ticks_for_irq: %d\n", _acpi_hpet->min_clock_ticks_for_irq);
158   printf("  page_prot_and_oem_attr:  %x\n", _acpi_hpet->page_prot_and_oem_attr);
159 }
160
161 PUBLIC static FIASCO_INIT
162 bool
163 Hpet::init()
164 {
165   _acpi_hpet = Acpi::find<Acpi_hpet const *>("HPET");
166
167   if (!_acpi_hpet)
168     {
169       printf("Could not find HPET in RSDT nor XSDT, skipping init\n");
170       return false;
171     }
172
173   dump_acpi_infos();
174
175   Address offs;
176   Address a = _acpi_hpet->base_address.addr;
177   Address va = Mem_layout::alloc_io_vmem(Config::PAGE_SIZE);
178   assert (va);
179  
180   Kmem::map_phys_page(a, va, false, true, &offs);
181
182   Kip::k()->add_mem_region(Mem_desc(a, a + 1023, Mem_desc::Reserved));
183
184   _hpet = (Hpet_device *)(va + offs);
185
186   _hpet->dump();
187
188   _hpet->disable();
189   _hpet->reset_counter();
190
191   int i = 2;
192   Hpet_timer *t = 0;
193   for (; i < _hpet->num_timers(); ++i)
194     {
195       t = _hpet->timer(i);
196
197       if (t->can_periodic() && t->int_route_cap())
198         {
199           t->force_32bit();
200           t->set_periodic();
201           t->set_edge_irq();
202           t->val_set();
203           t->set_int(t->get_first_int());
204
205           t->comp_val = div32(1000000000000ULL, _hpet->counter_clk_period());
206
207           break;
208         }
209     }
210
211   if (!t)
212     {
213       printf("ERROR: Did not find a HPET timer that can do periodic mode.\n");
214       return false;
215     }
216
217   _hpet_timer = t;
218   _hpet->dump();
219
220   return true;
221 }