1/** 2 * \file 3 * \brief x86 legacy timer driver. 4 */ 5 6/* 7 * Copyright (c) 2007, 2008, 2009, ETH Zurich. 8 * All rights reserved. 9 * 10 * This file is distributed under the terms in the attached LICENSE file. 11 * If you do not find this file, copies can be found by writing to: 12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15#include <stdio.h> 16#include <barrelfish/barrelfish.h> 17#include <pci/pci.h> 18#include <dev/lpc_timer_dev.h> 19#include "timer.h" 20 21#include "lpc_timer_debug.h" 22 23#define TIMER_IOBASE 0x40 24 25/// Period of LPC timer 0 counter, in nanoseconds 26#define TIMER0_PERIOD_NS 838 27 28/// Maximum value of (16-bit) count 29#define TIMER_MAX_COUNT ((1 << 16) - 1) 30 31 32/*************************************************************//** 33 * \defGroup LocalStates Local states 34 * 35 * @{ 36 * 37 ****************************************************************/ 38 39static struct lpc_timer_t timer; ///< Mackerel state for timer registers 40static timer_handler_fn timer_handler; ///< Expiry handler 41 42/// Remaining value of current timeout, in timer ticks 43static uint64_t timer_remainder; 44 45/* 46 * @} 47 */ 48 49 50/*************************************************************//** 51 * \defGroup Main Main 52 * 53 * @{ 54 * 55 ****************************************************************/ 56 57 58/** 59 * \brief Set hardware timer mode and count value 60 * 61 * Sets timer 0 as either a rate generator (mode 2) or one-shot timer (mode 0) 62 * and sets its value. This function currently does not deal with any other 63 * timers. 64 * 65 * \param count Count for oneshot timer, rate for ticker 66 * \param periodic True for a periodic timer, false for oneshot 67 */ 68static void timer0_set(uint16_t count, bool periodic) 69{ 70 /* 71 LPC_DEBUG("timer0_set: programming %s timer for %u ticks\n", 72 periodic ? "periodic" : "one-shot", count); 73*/ 74 struct lpc_timer_tcw_t tcw = { 75 .bcd = 0, // Binary mode (no BCD) 76 .mode = periodic ? lpc_timer_rtgen : lpc_timer_oseoc, // Operating mode 77 .rwsel = lpc_timer_lmsb, // First MSB, then LSB 78 .select = lpc_timer_c0 // Select counter 0 79 }; 80 81 // Prepare timer 0 to set its count 82 lpc_timer_tcw_wr(&timer, tcw); 83 84 if (count > 0) { 85 // Set the count/rate (LSB, then MSB) 86 lpc_timer_cntacc0_wr(&timer, count & 0xff); 87 lpc_timer_cntacc0_wr(&timer, count >> 8); 88 } 89} 90 91 92/** 93 * \brief Read current value of timer 94 * 95 * \returns the current value of timer 0 96 */ 97static uint16_t timer0_read(void) 98{ 99 uint16_t val; 100 lpc_timer_sbyte_fmt_t status; 101 102 do { 103 // 1. Issue read back command to read the status and count of the counter 104 struct lpc_timer_rdbk_cmd_t cmd = { 105 .c0 = 1, .c1 = 0, .c2 = 0, // select counter 0 only 106 .stat = 0, .count = 0 // latch both status and count 107 }; 108 lpc_timer_rdbk_cmd_wr(&timer, cmd); 109 110 // 2. Read status 111 status = lpc_timer_sbyte_fmt0_rd(&timer); 112 113 // 3. Read value latched value (LSB, then MSB) 114 // (we must do this even if the status shows an invalid count) 115 val = lpc_timer_cntacc0_rd(&timer) << 8; 116 val |= lpc_timer_cntacc0_rd(&timer); 117 118 LPC_DEBUG("timer0_read:%s %u ticks remaining\n", 119 status.cnt_stat ? " null count read," : "", val); 120 121 // if we got unlucky, and read the counter before it had finished loading, 122 // the count may be invalid ("null count"), so we repeat the whole rigmarole 123 } while (status.cnt_stat); 124 125 return val; 126} 127 128 129/** 130 * \brief message handler for timer interrupt 131 * 132 * 133 * 134 */ 135static void lpc_timer_interrupt(void *arg) 136{ 137// LPC_DEBUG("interrupt\n"); 138 139 // reprogram timer if remainder is set 140 if (timer_remainder != 0) { 141 if (timer_remainder > TIMER_MAX_COUNT) { 142 timer0_set(TIMER_MAX_COUNT, false); 143 timer_remainder -= TIMER_MAX_COUNT; 144 } else { 145 timer0_set(timer_remainder, false); 146 timer_remainder = 0; 147 } 148 // otherwise we have a timeout: run the handler 149 } else if (timer_handler != NULL) { 150 timer_handler(); 151 } else { 152 LPC_DEBUG("timer_interrupt: no handler\n"); 153 } 154} 155 156 157/** 158 * \brief 159 * 160 * 161 * 162 */ 163void lpc_timer_register_handler(timer_handler_fn handler) 164{ 165 LPC_DEBUG("timer_register_handler: called\n"); 166 167 timer_handler = handler; 168 169 LPC_DEBUG("timer_register_handler: terminated\n"); 170} 171 172static void timer_init(void) 173{ 174 LPC_DEBUG("timer_init: called\n"); 175 176 lpc_timer_initialize(&timer, TIMER_IOBASE); 177 timer0_set(0, false); 178 179 timer_init_complete(); 180 181 LPC_DEBUG("timer_init: terminated\n"); 182} 183 184/** 185 * \brief Initialize timer driver. 186 * 187 * Initializes the timer (and driver) to a known state. 188 */ 189errval_t lpc_timer_init(void) 190{ 191 int r = pci_client_connect(); 192 assert(r == 0); // XXX 193 194 return pci_register_legacy_driver_irq_cap(timer_init, TIMER_IOBASE, 195 TIMER_IOBASE + 4, 0, 196 lpc_timer_interrupt, NULL); 197} 198 199 200/** 201 * \brief Set the timeout. 202 * 203 * \param us Timeout in microseconds 204 */ 205void lpc_timer_set(uint64_t us) 206{ 207 // the requested time must not exceed the maximum possible interval 208 assert (us < UINT64_MAX / (1000 / TIMER0_PERIOD_NS)); 209 210 // convert to timer count value 211 uint64_t count = us * 1000 / TIMER0_PERIOD_NS; 212 213 // program hardware timer for max(TIMER_MAX_COUNT, count) 214 if (count > TIMER_MAX_COUNT) { 215 timer_remainder = count - TIMER_MAX_COUNT; 216 count = TIMER_MAX_COUNT; 217 } else { 218 timer_remainder = 0; 219 } 220 221 timer0_set(count, false); 222 223 LPC_DEBUG("lpc_timer_set: %lu us -> %lu + %lu ticks\n", 224 us, count, timer_remainder); 225} 226 227 228/** 229 * \brief Read the current value of the timer 230 * 231 * \returns Remaining time of pending timer, in microseconds 232 */ 233uint64_t lpc_timer_read(void) 234{ 235 uint16_t val = timer0_read(); 236 uint64_t us = (val + timer_remainder) * TIMER0_PERIOD_NS / 1000; 237 238 LPC_DEBUG("lpc_timer_read: %u ticks + %lu remaining = %lu us\n", 239 val, timer_remainder, us); 240 241 return us; 242} 243 244/* 245 * @} 246 */ 247