sp804tmr.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /*++
  2. Copyright (c) 2012 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. sp804tmr.c
  5. Abstract:
  6. This module implements support for the ARM SP804 dual timer.
  7. Author:
  8. Evan Green 22-Aug-2012
  9. Environment:
  10. Kernel
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. //
  16. // Include kernel.h, but be cautious about which APIs are used. Most of the
  17. // system depends on the hardware modules. Limit use to HL, RTL and AR routines.
  18. //
  19. #include <minoca/kernel/kernel.h>
  20. #include "realview.h"
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. #define SP804_ALLOCATION_TAG 0x34385053 // '48PS'
  25. //
  26. // Control register bits.
  27. //
  28. #define SP804_CONTROL_ENABLED 0x80
  29. #define SP804_CONTROL_MODE_FREE_RUNNING 0x00
  30. #define SP804_CONTROL_MODE_PERIODIC 0x40
  31. #define SP804_CONTROL_INTERRUPT_ENABLE 0x20
  32. #define SP804_CONTROL_DIVIDE_BY_1 0x00
  33. #define SP804_CONTROL_DIVIDE_BY_16 0x04
  34. #define SP804_CONTROL_DIVIDE_BY_256 0x08
  35. #define SP804_CONTROL_32_BIT 0x02
  36. #define SP804_CONTROL_16_BIT 0x00
  37. #define SP804_CONTROL_MODE_ONE_SHOT 0x01
  38. //
  39. // --------------------------------------------------------------------- Macros
  40. //
  41. //
  42. // This macro reads from an SP804 timer. _Base should be a pointer, and
  43. // _Register should be a SP804_TIMER_REGISTER value.
  44. //
  45. #define READ_TIMER_REGISTER(_Base, _Register) \
  46. HlReadRegister32((PULONG)(_Base) + (_Register))
  47. //
  48. // This macro writes to a SP804 timer. _Base should be a pointer,
  49. // _Register should be SP804_TIMER_REGISTER value, and _Value should be a ULONG.
  50. //
  51. #define WRITE_TIMER_REGISTER(_Base, _Register, _Value) \
  52. HlWriteRegister32((PULONG)(_Base) + (_Register), (_Value))
  53. //
  54. // ----------------------------------------------- Internal Function Prototypes
  55. //
  56. KSTATUS
  57. HlpSp804TimerInitialize (
  58. PVOID Context
  59. );
  60. ULONGLONG
  61. HlpSp804TimerRead (
  62. PVOID Context
  63. );
  64. KSTATUS
  65. HlpSp804TimerArm (
  66. PVOID Context,
  67. TIMER_MODE Mode,
  68. ULONGLONG TickCount
  69. );
  70. VOID
  71. HlpSp804TimerDisarm (
  72. PVOID Context
  73. );
  74. VOID
  75. HlpSp804TimerAcknowledgeInterrupt (
  76. PVOID Context
  77. );
  78. //
  79. // ------------------------------------------------------ Data Type Definitions
  80. //
  81. //
  82. // Define the registers for one timer, in ULONGs.
  83. //
  84. typedef enum _SP804_TIMER_REGISTER {
  85. Sp804LoadValue = 0,
  86. Sp804CurrentValue = 1,
  87. Sp804Control = 2,
  88. Sp804InterruptClear = 3,
  89. Sp804InterruptRawStatus = 4,
  90. Sp804InterruptStatus = 5,
  91. Sp804BackgroundLoadValue = 6,
  92. Sp804RegisterSize = 0x20
  93. } SP804_TIMER_REGISTER, *PSP804_TIMER_REGISTER;
  94. /*++
  95. Structure Description:
  96. This structure stores the internal state associated with a SP804 timer.
  97. Members:
  98. PhysicalAddress - Stores the physical address of the timer base.
  99. BaseAddress - Stores the virtual address of the beginning of this timer
  100. block.
  101. Index - Stores the zero-based index of this timer within the timer block.
  102. --*/
  103. typedef struct _SP804_TIMER_DATA {
  104. PHYSICAL_ADDRESS PhysicalAddress;
  105. PVOID BaseAddress;
  106. ULONG Index;
  107. } SP804_TIMER_DATA, *PSP804_TIMER_DATA;
  108. //
  109. // -------------------------------------------------------------------- Globals
  110. //
  111. //
  112. // Store a pointer to the RealView table.
  113. //
  114. PREALVIEW_TABLE HlRealViewTable;
  115. //
  116. // ------------------------------------------------------------------ Functions
  117. //
  118. VOID
  119. HlpSp804TimerModuleEntry (
  120. VOID
  121. )
  122. /*++
  123. Routine Description:
  124. This routine is the entry point for the SP804 timer hardware module.
  125. Its role is to detect and report the prescense of an SP804 timer.
  126. Arguments:
  127. None.
  128. Return Value:
  129. None.
  130. --*/
  131. {
  132. PULONGLONG Frequencies;
  133. PULONGLONG PhysicalAddresses;
  134. KSTATUS Status;
  135. TIMER_DESCRIPTION Timer;
  136. ULONG TimerCount;
  137. PSP804_TIMER_DATA TimerData;
  138. PULONG TimerGsi;
  139. ULONG TimerIndex;
  140. if (HlRealViewTable == NULL) {
  141. HlRealViewTable = HlGetAcpiTable(REALVIEW_SIGNATURE, NULL);
  142. }
  143. if (HlRealViewTable != NULL) {
  144. Frequencies = HlRealViewTable->TimerFrequency;
  145. PhysicalAddresses = HlRealViewTable->TimerPhysicalAddress;
  146. TimerCount = REALVIEW_TIMER_COUNT;
  147. TimerGsi = HlRealViewTable->TimerGsi;
  148. } else {
  149. goto Sp804TimerModuleEntryEnd;
  150. }
  151. //
  152. // Register each of the independent timers in the timer block.
  153. //
  154. for (TimerIndex = 0; TimerIndex < TimerCount; TimerIndex += 1) {
  155. RtlZeroMemory(&Timer, sizeof(TIMER_DESCRIPTION));
  156. Timer.TableVersion = TIMER_DESCRIPTION_VERSION;
  157. Timer.FunctionTable.Initialize = HlpSp804TimerInitialize;
  158. Timer.FunctionTable.ReadCounter = HlpSp804TimerRead;
  159. Timer.FunctionTable.WriteCounter = NULL;
  160. Timer.FunctionTable.Arm = HlpSp804TimerArm;
  161. Timer.FunctionTable.Disarm = HlpSp804TimerDisarm;
  162. Timer.FunctionTable.AcknowledgeInterrupt =
  163. HlpSp804TimerAcknowledgeInterrupt;
  164. //
  165. // Each timer block is actually two timers. Allocate a single structure
  166. // for both, and know that if the index is one, go backwards to get
  167. // to the main information.
  168. //
  169. TimerData = HlAllocateMemory(sizeof(SP804_TIMER_DATA) * 2,
  170. SP804_ALLOCATION_TAG,
  171. FALSE,
  172. NULL);
  173. if (TimerData == NULL) {
  174. Status = STATUS_INSUFFICIENT_RESOURCES;
  175. goto Sp804TimerModuleEntryEnd;
  176. }
  177. RtlZeroMemory(TimerData, sizeof(SP804_TIMER_DATA) * 2);
  178. TimerData->PhysicalAddress = PhysicalAddresses[TimerIndex];
  179. TimerData->Index = 0;
  180. TimerData[1].Index = 1;
  181. Timer.Context = TimerData;
  182. Timer.Features = TIMER_FEATURE_READABLE |
  183. TIMER_FEATURE_PERIODIC |
  184. TIMER_FEATURE_ONE_SHOT;
  185. Timer.CounterBitWidth = 32;
  186. Timer.CounterFrequency = Frequencies[TimerIndex];
  187. Timer.Interrupt.Line.Type = InterruptLineGsi;
  188. Timer.Interrupt.Line.U.Gsi = TimerGsi[TimerIndex];
  189. Timer.Interrupt.TriggerMode = InterruptModeUnknown;
  190. Timer.Interrupt.ActiveLevel = InterruptActiveLevelUnknown;
  191. //
  192. // Register the timer with the system.
  193. //
  194. Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
  195. if (!KSUCCESS(Status)) {
  196. goto Sp804TimerModuleEntryEnd;
  197. }
  198. //
  199. // Register the second one. Report it as not having interrupt
  200. // capabilities as there's no way to disambiguate between the two
  201. // timers when the interrupt comes in.
  202. //
  203. Timer.Context = TimerData + 1;
  204. Timer.Features = TIMER_FEATURE_READABLE;
  205. Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
  206. if (!KSUCCESS(Status)) {
  207. goto Sp804TimerModuleEntryEnd;
  208. }
  209. }
  210. Sp804TimerModuleEntryEnd:
  211. return;
  212. }
  213. //
  214. // --------------------------------------------------------- Internal Functions
  215. //
  216. KSTATUS
  217. HlpSp804TimerInitialize (
  218. PVOID Context
  219. )
  220. /*++
  221. Routine Description:
  222. This routine initializes an SP804 timer.
  223. Arguments:
  224. Context - Supplies the pointer to the timer's context, provided by the
  225. hardware module upon initialization.
  226. Return Value:
  227. Status code.
  228. --*/
  229. {
  230. PVOID Base;
  231. ULONG ControlValue;
  232. BOOL SecondTimer;
  233. KSTATUS Status;
  234. PSP804_TIMER_DATA Timer;
  235. //
  236. // If this is the second timer, go back a structure to get the real one.
  237. //
  238. SecondTimer = FALSE;
  239. Timer = (PSP804_TIMER_DATA)Context;
  240. if (Timer->Index == 1) {
  241. Timer -= 1;
  242. SecondTimer = TRUE;
  243. }
  244. //
  245. // Map the hardware if that has not been done.
  246. //
  247. if (Timer->BaseAddress == NULL) {
  248. Timer->BaseAddress = HlMapPhysicalAddress(
  249. Timer->PhysicalAddress,
  250. 2 * Sp804RegisterSize * sizeof(ULONG),
  251. TRUE);
  252. if (Timer->BaseAddress == NULL) {
  253. Status = STATUS_INSUFFICIENT_RESOURCES;
  254. goto Sp804TimerInitializeEnd;
  255. }
  256. }
  257. Base = Timer->BaseAddress;
  258. if (SecondTimer != FALSE) {
  259. Base += Sp804RegisterSize;
  260. }
  261. //
  262. // Program the timer in free running mode with no interrupt generation.
  263. //
  264. ControlValue = SP804_CONTROL_ENABLED |
  265. SP804_CONTROL_DIVIDE_BY_1 |
  266. SP804_CONTROL_32_BIT |
  267. SP804_CONTROL_MODE_FREE_RUNNING;
  268. WRITE_TIMER_REGISTER(Base, Sp804Control, ControlValue);
  269. WRITE_TIMER_REGISTER(Base, Sp804InterruptClear, 1);
  270. Status = STATUS_SUCCESS;
  271. Sp804TimerInitializeEnd:
  272. return Status;
  273. }
  274. ULONGLONG
  275. HlpSp804TimerRead (
  276. PVOID Context
  277. )
  278. /*++
  279. Routine Description:
  280. This routine returns the hardware counter's raw value.
  281. Arguments:
  282. Context - Supplies the pointer to the timer's context.
  283. Return Value:
  284. Returns the timer's current count.
  285. --*/
  286. {
  287. PVOID Base;
  288. PSP804_TIMER_DATA Timer;
  289. ULONG Value;
  290. //
  291. // If this is the second timer, go back a structure to get the real one.
  292. //
  293. Timer = (PSP804_TIMER_DATA)Context;
  294. Base = Timer->BaseAddress;
  295. if (Timer->Index == 1) {
  296. Timer -= 1;
  297. Base = Timer->BaseAddress + Sp804RegisterSize;
  298. }
  299. Value = 0xFFFFFFFF - READ_TIMER_REGISTER(Base, Sp804CurrentValue);
  300. return Value;
  301. }
  302. KSTATUS
  303. HlpSp804TimerArm (
  304. PVOID Context,
  305. TIMER_MODE Mode,
  306. ULONGLONG TickCount
  307. )
  308. /*++
  309. Routine Description:
  310. This routine arms the timer to fire an interrupt after the specified number
  311. of ticks.
  312. Arguments:
  313. Context - Supplies the pointer to the timer's context, provided by the
  314. hardware module upon initialization.
  315. Mode - Supplies the mode to arm the timer in. The system will never request
  316. a mode not supported by the timer's feature bits. The mode dictates
  317. how the tick count argument is interpreted.
  318. TickCount - Supplies the number of timer ticks from now for the timer to
  319. fire in. In absolute mode, this supplies the time in timer ticks at
  320. which to fire an interrupt.
  321. Return Value:
  322. STATUS_SUCCESS always.
  323. --*/
  324. {
  325. PVOID Base;
  326. ULONG ControlValue;
  327. PSP804_TIMER_DATA Timer;
  328. //
  329. // If this is the second timer, go back a structure to get the real one.
  330. //
  331. Timer = (PSP804_TIMER_DATA)Context;
  332. Base = Timer->BaseAddress;
  333. if (Timer->Index == 1) {
  334. Timer -= 1;
  335. Base = Timer->BaseAddress + Sp804RegisterSize;
  336. }
  337. if (TickCount >= MAX_ULONG) {
  338. TickCount = MAX_ULONG - 1;
  339. }
  340. //
  341. // Set up the control value to program.
  342. //
  343. ControlValue = SP804_CONTROL_ENABLED |
  344. SP804_CONTROL_DIVIDE_BY_1 |
  345. SP804_CONTROL_32_BIT |
  346. SP804_CONTROL_INTERRUPT_ENABLE;
  347. if (Mode == TimerModePeriodic) {
  348. ControlValue |= SP804_CONTROL_MODE_PERIODIC;
  349. } else {
  350. ControlValue |= SP804_CONTROL_MODE_ONE_SHOT;
  351. }
  352. //
  353. // Set the timer to its maximum value, set the configuration, clear the
  354. // interrupt, then set the value.
  355. //
  356. WRITE_TIMER_REGISTER(Base, Sp804LoadValue, 0xFFFFFFFF);
  357. WRITE_TIMER_REGISTER(Base, Sp804Control, ControlValue);
  358. WRITE_TIMER_REGISTER(Base, Sp804InterruptClear, 1);
  359. WRITE_TIMER_REGISTER(Base, Sp804LoadValue, TickCount);
  360. return STATUS_SUCCESS;
  361. }
  362. VOID
  363. HlpSp804TimerDisarm (
  364. PVOID Context
  365. )
  366. /*++
  367. Routine Description:
  368. This routine disarms the timer, stopping interrupts from firing.
  369. Arguments:
  370. Context - Supplies the pointer to the timer's context, provided by the
  371. hardware module upon initialization.
  372. Return Value:
  373. None.
  374. --*/
  375. {
  376. ULONG ControlValue;
  377. PSP804_TIMER_DATA Timer;
  378. Timer = (PSP804_TIMER_DATA)Context;
  379. //
  380. // Disable the timer by programming it in free running mode with no
  381. // interrupt generation.
  382. //
  383. ControlValue = SP804_CONTROL_ENABLED |
  384. SP804_CONTROL_DIVIDE_BY_1 |
  385. SP804_CONTROL_32_BIT |
  386. SP804_CONTROL_MODE_FREE_RUNNING;
  387. WRITE_TIMER_REGISTER(Timer->BaseAddress, Sp804Control, ControlValue);
  388. WRITE_TIMER_REGISTER(Timer->BaseAddress, Sp804InterruptClear, 1);
  389. return;
  390. }
  391. VOID
  392. HlpSp804TimerAcknowledgeInterrupt (
  393. PVOID Context
  394. )
  395. /*++
  396. Routine Description:
  397. This routine performs any actions necessary upon reciept of a timer's
  398. interrupt. This may involve writing to an acknowledge register to re-enable
  399. the timer to fire again, or other hardware specific actions.
  400. Arguments:
  401. Context - Supplies the pointer to the timer's context, provided by the
  402. hardware module upon initialization.
  403. Return Value:
  404. None.
  405. --*/
  406. {
  407. PVOID Base;
  408. PSP804_TIMER_DATA Timer;
  409. //
  410. // If this is the second timer, go back a structure to get the real one.
  411. //
  412. Timer = (PSP804_TIMER_DATA)Context;
  413. Base = Timer->BaseAddress;
  414. if (Timer->Index == 1) {
  415. Timer -= 1;
  416. Base = Timer->BaseAddress + Sp804RegisterSize;
  417. }
  418. WRITE_TIMER_REGISTER(Base, Sp804InterruptClear, 1);
  419. return;
  420. }