Handout
This commit is contained in:
226
kernel/arch/pit.cc
Normal file
226
kernel/arch/pit.cc
Normal file
@@ -0,0 +1,226 @@
|
||||
#include "pit.h"
|
||||
|
||||
#include "core.h"
|
||||
#include "ioport.h"
|
||||
|
||||
namespace PIT {
|
||||
|
||||
// we only use PIT channel 2
|
||||
const uint8_t CHANNEL = 2;
|
||||
static IOPort data(0x40 + CHANNEL);
|
||||
|
||||
/*! \brief Access mode
|
||||
*/
|
||||
enum AccessMode {
|
||||
LATCH_COUNT_VALUE = 0,
|
||||
LOW_BYTE_ONLY = 1,
|
||||
HIGH_BYTE_ONLY = 2,
|
||||
LOW_AND_HIGH_BYTE = 3
|
||||
};
|
||||
|
||||
/*! \brief Operating Mode
|
||||
*
|
||||
* \warning Channel 2 is not able to send interrupts, however, the status bit
|
||||
* will be set
|
||||
*/
|
||||
enum OperatingMode {
|
||||
INTERRUPT_ON_TERMINAL_COUNT = 0,
|
||||
PROGRAMMABLE_ONE_SHOT = 1,
|
||||
RATE_GENERATOR = 2,
|
||||
SQUARE_WAVE_GENERATOR = 3, ///< useful for the PC speaker
|
||||
SOFTWARE_TRIGGERED_STROBE = 4,
|
||||
HARDWARE_TRIGGERED_STROBE = 5
|
||||
};
|
||||
|
||||
/*! \brief data format
|
||||
*/
|
||||
enum Format {
|
||||
BINARY = 0,
|
||||
BCD = 1 ///< Binary Coded Decimals
|
||||
};
|
||||
|
||||
// Mode register (only writable)
|
||||
static IOPort mode_register(0x43);
|
||||
union Mode {
|
||||
struct {
|
||||
Format format : 1;
|
||||
OperatingMode operating : 3;
|
||||
AccessMode access : 2;
|
||||
uint8_t channel : 2;
|
||||
};
|
||||
uint8_t value;
|
||||
|
||||
/*! \brief Constructor for mode, takes the numeric value */
|
||||
explicit Mode(uint8_t value) : value(value) {}
|
||||
|
||||
/*! \brief Constructor for counting mode
|
||||
* \param access Access mode to the 16-bit counter value
|
||||
* \param operating Operating mode for the counter
|
||||
* \param format Number format for the 16-bit counter values (binary or
|
||||
* BCD)
|
||||
*/
|
||||
Mode(AccessMode access, OperatingMode operating, Format format)
|
||||
: format(format),
|
||||
operating(operating),
|
||||
access(access),
|
||||
channel(PIT::CHANNEL) {}
|
||||
|
||||
/*! \brief (Default) constructor for reading the counter value
|
||||
*/
|
||||
Mode() : value(0) { this->channel = PIT::CHANNEL; }
|
||||
|
||||
/*! \brief Write the value to the mode register
|
||||
*/
|
||||
void write() const { mode_register.outb(value); }
|
||||
};
|
||||
|
||||
// The NMI Status and Control Register contains details about PIT counter 2
|
||||
static IOPort controlRegister(0x61);
|
||||
union Control {
|
||||
/*! \brief I/O-port bitmap for the NMI Status and Control Register
|
||||
* \note Over time, the meaning of the bits stored at I/O port 0x61
|
||||
* changed; don't get the structure confused with old documentation on the
|
||||
* IBM PC XT platform.
|
||||
* \see [Intel® I/O Controller Hub 7 (ICH7)
|
||||
* Family](i-o-controller-hub-7-datasheet.pdf#page=415), page 415
|
||||
*/
|
||||
struct {
|
||||
//! If enabled, the interrupt state will be visible at
|
||||
//! status_timer_counter2
|
||||
uint8_t enable_timer_counter2 : 1;
|
||||
uint8_t enable_speaker_data : 1; ///< If set, speaker output is equal
|
||||
///< to status_timer_counter2
|
||||
uint8_t enable_pci_serr : 1; ///< not important, do not modify
|
||||
uint8_t enable_nmi_iochk : 1; ///< not important, do not modify
|
||||
const uint8_t
|
||||
refresh_cycle_toggle : 1; ///< not important, must be 0 on write
|
||||
const uint8_t
|
||||
status_timer_counter2 : 1; ///< will be set on timer expiration;
|
||||
///< must be 0 on write
|
||||
const uint8_t
|
||||
status_iochk_nmi_source : 1; ///< not important, must be 0 on write
|
||||
const uint8_t
|
||||
status_serr_nmi_source : 1; ///< not important, must be 0 on write
|
||||
};
|
||||
uint8_t value;
|
||||
|
||||
/*! \brief Constructor
|
||||
* \param value Numeric value for the control register
|
||||
*/
|
||||
explicit Control(uint8_t value) : value(value) {}
|
||||
|
||||
/*! \brief Default constructor
|
||||
* Automatically reads the current contents from the control register.
|
||||
*/
|
||||
Control() : value(controlRegister.inb()) {}
|
||||
|
||||
/*! \brief Write the current state to the control register.
|
||||
*/
|
||||
void write() const { controlRegister.outb(value); }
|
||||
};
|
||||
|
||||
// The base frequency is, due to historic reasons, 1.193182 MHz.
|
||||
const uint64_t BASE_FREQUENCY = 1193182ULL;
|
||||
|
||||
bool set(uint16_t us) {
|
||||
// Counter ticks for us
|
||||
uint64_t counter = BASE_FREQUENCY * us / 1000000ULL;
|
||||
|
||||
// As the hardware counter has a size of 16 bit, we want to check whether
|
||||
// the calculated counter value is too large ( > 54.9ms )
|
||||
if (counter > 0xffff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Interrupt state should be readable in status register, but PC speaker
|
||||
// should remain off
|
||||
Control c;
|
||||
c.enable_speaker_data = 0;
|
||||
c.enable_timer_counter2 = 1;
|
||||
c.write();
|
||||
|
||||
// Channel 2, 16-bit divisor, with mode 0 (interrupt) in binary format
|
||||
Mode m(AccessMode::LOW_AND_HIGH_BYTE,
|
||||
OperatingMode::INTERRUPT_ON_TERMINAL_COUNT, Format::BINARY);
|
||||
m.write();
|
||||
|
||||
// Set the counter's start value
|
||||
data.outb(counter & 0xff); // low
|
||||
data.outb((counter >> 8) & 0xff); // high
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t get(void) {
|
||||
// Set mode to reading
|
||||
Mode m;
|
||||
m.write();
|
||||
|
||||
// Read low and high
|
||||
uint16_t value = data.inb();
|
||||
value |= data.inb() << 8;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool isActive(void) {
|
||||
Control c; // reads the current value from the control register
|
||||
return c.enable_timer_counter2 == 1 && c.status_timer_counter2 == 0;
|
||||
}
|
||||
|
||||
bool waitForTimeout(void) {
|
||||
while (true) {
|
||||
Control c; // reads the current value from the control register
|
||||
if (c.enable_timer_counter2 == 0) {
|
||||
return false;
|
||||
} else if (c.status_timer_counter2 == 1) {
|
||||
return true;
|
||||
} else {
|
||||
Core::pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool delay(uint16_t us) { return set(us) && waitForTimeout(); }
|
||||
|
||||
void pcspeaker(uint32_t freq) {
|
||||
Control c;
|
||||
if (freq == 0) {
|
||||
disable();
|
||||
} else {
|
||||
// calculate frequency divider
|
||||
uint64_t div = BASE_FREQUENCY / freq;
|
||||
if (div > 0xffff) {
|
||||
div = 0xffff;
|
||||
}
|
||||
|
||||
// check if already configured
|
||||
if (c.enable_speaker_data == 0) {
|
||||
// if not, set mode
|
||||
Mode m(AccessMode::LOW_AND_HIGH_BYTE,
|
||||
OperatingMode::SQUARE_WAVE_GENERATOR, Format::BINARY);
|
||||
m.write();
|
||||
}
|
||||
|
||||
// write frequency divider
|
||||
data.outb(div & 0xff);
|
||||
data.outb((div >> 8) & 0xff);
|
||||
|
||||
// already configured? (second part to prevent playing a wrong sound)
|
||||
if (c.enable_speaker_data == 0) {
|
||||
// activate PC speaker
|
||||
c.enable_speaker_data = 1;
|
||||
c.enable_timer_counter2 = 1;
|
||||
c.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void disable(void) {
|
||||
Control c;
|
||||
c.enable_speaker_data = 0;
|
||||
c.enable_timer_counter2 = 0;
|
||||
c.write();
|
||||
}
|
||||
|
||||
} // namespace PIT
|
||||
Reference in New Issue
Block a user