Serenity Operating System
1/*
2 * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <Kernel/Assertions.h>
28#include <Kernel/Time/HPETComparator.h>
29#include <Kernel/Time/TimeManagement.h>
30
31//#define HPET_COMPARATOR_DEBUG
32
33namespace Kernel {
34
35NonnullRefPtr<HPETComparator> HPETComparator::create(u8 number, u8 irq, bool periodic_capable)
36{
37 return adopt(*new HPETComparator(number, irq, periodic_capable, [](const RegisterState& regs) { TimeManagement::stale_function(regs); }));
38}
39
40HPETComparator::HPETComparator(u8 number, u8 irq, bool periodic_capable, Function<void(const RegisterState&)> callback)
41 : HardwareTimer(irq, move(callback))
42 , m_periodic(false)
43 , m_periodic_capable(periodic_capable)
44 , m_comparator_number(number)
45{
46}
47
48void HPETComparator::set_periodic()
49{
50 ASSERT(m_periodic_capable);
51 HPET::the().enable_periodic_interrupt(*this);
52 m_periodic = true;
53}
54void HPETComparator::set_non_periodic()
55{
56 ASSERT(m_periodic_capable);
57 HPET::the().disable_periodic_interrupt(*this);
58 m_periodic = false;
59}
60
61void HPETComparator::handle_irq(const RegisterState& regs)
62{
63 HardwareTimer::handle_irq(regs);
64 if (!is_periodic())
65 set_new_countdown();
66}
67
68void HPETComparator::set_new_countdown()
69{
70 ASSERT_INTERRUPTS_DISABLED();
71 ASSERT(m_frequency <= HPET::the().frequency());
72 HPET::the().set_non_periodic_comparator_value(*this, HPET::the().frequency() / m_frequency);
73}
74
75size_t HPETComparator::ticks_per_second() const
76{
77 return m_frequency;
78}
79
80void HPETComparator::reset_to_default_ticks_per_second()
81{
82 ASSERT(is_capable_of_frequency(OPTIMAL_TICKS_PER_SECOND_RATE));
83 m_frequency = OPTIMAL_TICKS_PER_SECOND_RATE;
84 if (!is_periodic())
85 set_new_countdown();
86 else
87 try_to_set_frequency(m_frequency);
88}
89bool HPETComparator::try_to_set_frequency(size_t frequency)
90{
91 InterruptDisabler disabler;
92 if (!is_capable_of_frequency(frequency))
93 return false;
94 disable_irq();
95 auto hpet_frequency = HPET::the().frequency();
96 ASSERT(frequency <= hpet_frequency);
97#ifdef HPET_COMPARATOR_DEBUG
98 dbg() << "HPET Comparator: Max frequency - " << hpet_frequency << " Hz, want to set " << frequency << " Hz";
99#endif
100 if (is_periodic())
101 HPET::the().set_periodic_comparator_value(*this, hpet_frequency / frequency);
102 else {
103 HPET::the().set_non_periodic_comparator_value(*this, hpet_frequency / frequency);
104 HPET::the().enable(*this);
105 }
106 m_frequency = frequency;
107 enable_irq();
108 return true;
109}
110bool HPETComparator::is_capable_of_frequency(size_t frequency) const
111{
112 if (frequency > HPET::the().frequency())
113 return false;
114 if ((HPET::the().frequency() % frequency) != 0)
115 return false;
116 return true;
117}
118size_t HPETComparator::calculate_nearest_possible_frequency(size_t frequency) const
119{
120 if (frequency >= HPET::the().frequency())
121 return HPET::the().frequency();
122 // FIXME: Use better math here
123 return (frequency + (HPET::the().frequency() % frequency));
124}
125
126}