b2709tmr.c 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. b2709tmr.c
  5. Abstract:
  6. This module implements support for the BCM2709's timers.
  7. Author:
  8. Chris Stevens 24-Mar-2014
  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 "bcm2709.h"
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. //
  25. // ARM Timer Control register bits.
  26. //
  27. // The BCM2709's version of the SP804 does not support one-shot mode and is
  28. // always periodic based on the load value, making those bits defunct. It also
  29. // introduces extra control bits for controlling its extra free-running counter.
  30. //
  31. #define BCM2709_ARM_TIMER_CONTROL_FREE_RUNNING_DIVIDE_MASK 0x00FF0000
  32. #define BCM2709_ARM_TIMER_CONTROL_FREE_RUNNING_DIVIDE_SHIFT 16
  33. #define BCM2709_ARM_TIMER_CONTROL_FREE_RUNNING_ENABLED 0x00000200
  34. #define BCM2709_ARM_TIMER_CONTROL_HALT_ON_DEBUG 0x00000100
  35. #define BCM2709_ARM_TIMER_CONTROL_ENABLED 0x00000080
  36. #define BCM2709_ARM_TIMER_CONTROL_INTERRUPT_ENABLE 0x00000020
  37. #define BCM2709_ARM_TIMER_CONTROL_DIVIDE_BY_1 0x00000000
  38. #define BCM2709_ARM_TIMER_CONTROL_DIVIDE_BY_16 0x00000004
  39. #define BCM2709_ARM_TIMER_CONTROL_DIVIDE_BY_256 0x00000008
  40. #define BCM2709_ARM_TIMER_CONTROL_32_BIT 0x00000002
  41. #define BCM2709_ARM_TIMER_CONTROL_16_BIT 0x00000000
  42. //
  43. // Define the target default frequency to use for the BCM2709 timer, if
  44. // possible.
  45. //
  46. #define BCM2709_ARM_TIMER_TARGET_FREQUENCY 1000000
  47. //
  48. // Define the maximum predivider.
  49. //
  50. #define BCM2709_ARM_TIMER_PREDIVIDER_MAX 0x1FF
  51. //
  52. // Define the BCM2709 System Timer's control register values.
  53. //
  54. #define BCM2709_SYSTEM_TIMER_CONTROL_MATCH_3 0x00000008
  55. #define BCM2709_SYSTEM_TIMER_CONTROL_MATCH_2 0x00000004
  56. #define BCM2709_SYSTEM_TIMER_CONTROL_MATCH_1 0x00000002
  57. #define BCM2709_SYSTEM_TIMER_CONTROL_MATCH_0 0x00000001
  58. //
  59. // --------------------------------------------------------------------- Macros
  60. //
  61. //
  62. // This macro reads from a BCM2709 ARM Timer register.
  63. //
  64. #define READ_ARM_TIMER_REGISTER(_Register) \
  65. HlReadRegister32(HlBcm2709ArmTimerBase + (_Register))
  66. //
  67. // This macro writes to a BCM2709 ARM Timer register.
  68. //
  69. #define WRITE_ARM_TIMER_REGISTER(_Register, _Value) \
  70. HlWriteRegister32(HlBcm2709ArmTimerBase + (_Register), (_Value))
  71. //
  72. // This macro reads from a BCM2709 System Timer register.
  73. //
  74. #define READ_SYSTEM_TIMER_REGISTER(_Register) \
  75. HlReadRegister32(HlBcm2709SystemTimerBase + (_Register))
  76. //
  77. // This macro writes to a BCM2709 System Timer register.
  78. //
  79. #define WRITE_SYSTEM_TIMER_REGISTER(_Register, _Value) \
  80. HlWriteRegister32(HlBcm2709SystemTimerBase + (_Register), (_Value))
  81. //
  82. // This macro compares two counter values accounting for roll over.
  83. //
  84. #define BCM2709_COUNTER_LESS_THAN(_Counter1, _Counter2) \
  85. (((LONG)((_Counter1) - (_Counter2))) < 0)
  86. //
  87. // ----------------------------------------------- Internal Function Prototypes
  88. //
  89. KSTATUS
  90. HlpBcm2709TimerInitialize (
  91. PVOID Context
  92. );
  93. ULONGLONG
  94. HlpBcm2709TimerRead (
  95. PVOID Context
  96. );
  97. KSTATUS
  98. HlpBcm2709TimerArm (
  99. PVOID Context,
  100. TIMER_MODE Mode,
  101. ULONGLONG TickCount
  102. );
  103. VOID
  104. HlpBcm2709TimerDisarm (
  105. PVOID Context
  106. );
  107. VOID
  108. HlpBcm2709TimerAcknowledgeInterrupt (
  109. PVOID Context
  110. );
  111. //
  112. // ------------------------------------------------------ Data Type Definitions
  113. //
  114. typedef enum _BCM2709_TIMER_TYPE {
  115. Bcm2709TimerArmPeriodic,
  116. Bcm2709TimerArmCounter,
  117. Bcm2709TimerSystemPeriodic0,
  118. Bcm2709TimerSystemPeriodic1,
  119. Bcm2709TimerSystemPeriodic2,
  120. Bcm2709TimerSystemPeriodic3,
  121. Bcm2709TimerSystemCounter
  122. } BCM2709_TIMER_TYPE, *PBCM2709_TIMER_TYPE;
  123. //
  124. // Define the registers for the ARM timer, in bytes.
  125. //
  126. typedef enum _BCM2709_ARM_TIMER_REGISTER {
  127. Bcm2709ArmTimerLoadValue = 0x00,
  128. Bcm2709ArmTimerCurrentValue = 0x04,
  129. Bcm2709ArmTimerControl = 0x08,
  130. Bcm2709ArmTimerInterruptClear = 0x0C,
  131. Bcm2709ArmTimerInterruptRawStatus = 0x10,
  132. Bcm2709ArmTimerInterruptStatus = 0x14,
  133. Bcm2709ArmTimerBackgroundLoadValue = 0x18,
  134. Bcm2709ArmTimerPredivider = 0x1C,
  135. Bcm2709ArmTimerFreeRunningCounter = 0x20,
  136. Bcm2709ArmTimerRegisterSize = 0x24
  137. } BCM2709_ARM_TIMER_REGISTER, *PBCM2709_ARM_TIMER_REGISTER;
  138. //
  139. // Define the registers for the System timer, in bytes.
  140. //
  141. typedef enum _BCM2709_SYSTEM_TIMER_REGISTER {
  142. Bcm2709SystemTimerControl = 0x00,
  143. Bcm2709SystemTimerCounterLow = 0x04,
  144. Bcm2709SystemTimerCounterHigh = 0x08,
  145. Bcm2709SystemTimerCompare0 = 0x0C,
  146. Bcm2709SystemTimerCompare1 = 0x10,
  147. Bcm2709SystemTimerCompare2 = 0x14,
  148. Bcm2709SystemTimerCompare3 = 0x18,
  149. Bcm2709SystemTimerRegisterSize = 0x1C
  150. } BCM2709_SYSTEM_TIMER_REGISTER, *PBCM2709_SYSTEM_TIMER_REGISTER;
  151. /*++
  152. Structure Description:
  153. This structure defines a default BCM2709 timer.
  154. Members:
  155. Type - Stores the type of BCM2709 timer.
  156. Predivider - Stores the optional predivider used to program the frequency.
  157. --*/
  158. typedef struct _BCM2709_TIMER {
  159. BCM2709_TIMER_TYPE Type;
  160. ULONG Predivider;
  161. } BCM2709_TIMER, *PBCM2709_TIMER;
  162. /*++
  163. Structure Description:
  164. This structure defines a BCM2709 periodic system timer.
  165. Members:
  166. Type - Stores the type of BCM2709 timer.
  167. Mode - Stores the current mode.
  168. TickCount - Stores the current tick count. This is either a periodic
  169. interval or the relative one-shot tick count.
  170. Generation - Stores the generation counter used to synchronize attempts to
  171. arm and disarm the system timers with acknowledge interrupt rearming
  172. the timer for periodic mode.
  173. --*/
  174. typedef struct _BCM2709_SYSTEM_TIMER {
  175. BCM2709_TIMER_TYPE Type;
  176. TIMER_MODE Mode;
  177. ULONG TickCount;
  178. volatile ULONG Generation;
  179. } BCM2709_SYSTEM_TIMER, *PBCM2709_SYSTEM_TIMER;
  180. //
  181. // -------------------------------------------------------------------- Globals
  182. //
  183. //
  184. // Store the virtual address of the mapped timer bases.
  185. //
  186. PVOID HlBcm2709ArmTimerBase = NULL;
  187. PVOID HlBcm2709SystemTimerBase = NULL;
  188. //
  189. // ------------------------------------------------------------------ Functions
  190. //
  191. VOID
  192. HlpBcm2709TimerModuleEntry (
  193. VOID
  194. )
  195. /*++
  196. Routine Description:
  197. This routine is the entry point for the BCM2709 timer hardware module.
  198. Its role is to detect and report the prescense of the BCM2709 timer.
  199. Arguments:
  200. Services - Supplies a pointer to the services/APIs made available by the
  201. kernel to the hardware module.
  202. Return Value:
  203. None.
  204. --*/
  205. {
  206. PBCM2709_TIMER DefaultTimer;
  207. ULONGLONG Frequency;
  208. ULONGLONG MaxFrequency;
  209. ULONG Predivider;
  210. KSTATUS Status;
  211. PBCM2709_SYSTEM_TIMER SystemTimer;
  212. TIMER_DESCRIPTION Timer;
  213. //
  214. // Interrupt controllers are always initialized before timers, so the table
  215. // should already be set up.
  216. //
  217. if (HlBcm2709Table == NULL) {
  218. goto Bcm2709TimerModuleEntryEnd;
  219. }
  220. //
  221. // Initialize the ARM timers first. Determine the frequency based on the
  222. // APB clock frequency. The formula is "ARM Timer Frequency" =
  223. // ("APB Clock Frequency") / (Predivider + 1).
  224. //
  225. MaxFrequency = BCM2709_ARM_TIMER_TARGET_FREQUENCY *
  226. (BCM2709_ARM_TIMER_PREDIVIDER_MAX + 1);
  227. //
  228. // If the APB clock frequency is less than the target, just use that
  229. // frequency.
  230. //
  231. if (HlBcm2709Table->ApbClockFrequency <=
  232. BCM2709_ARM_TIMER_TARGET_FREQUENCY) {
  233. Frequency = HlBcm2709Table->ApbClockFrequency;
  234. Predivider = 0;
  235. //
  236. // If the APB clock frequency is less than or equal to the maximum
  237. // frequency that still allows the target frequency to be achieved, then
  238. // use the target frequency and associated predivider.
  239. //
  240. } else if (HlBcm2709Table->ApbClockFrequency <= MaxFrequency) {
  241. Frequency = BCM2709_ARM_TIMER_TARGET_FREQUENCY;
  242. Predivider = (HlBcm2709Table->ApbClockFrequency / Frequency) - 1;
  243. //
  244. // Otherwise get as close to the target frequency as possible by using the
  245. // maximum predivider.
  246. //
  247. } else {
  248. Predivider = BCM2709_ARM_TIMER_PREDIVIDER_MAX;
  249. Frequency = HlBcm2709Table->ApbClockFrequency / (Predivider + 1);
  250. }
  251. //
  252. // Register each of the independent timers in the timer block.
  253. //
  254. RtlZeroMemory(&Timer, sizeof(TIMER_DESCRIPTION));
  255. Timer.TableVersion = TIMER_DESCRIPTION_VERSION;
  256. Timer.FunctionTable.Initialize = HlpBcm2709TimerInitialize;
  257. Timer.FunctionTable.ReadCounter = HlpBcm2709TimerRead;
  258. Timer.FunctionTable.WriteCounter = NULL;
  259. Timer.FunctionTable.Arm = HlpBcm2709TimerArm;
  260. Timer.FunctionTable.Disarm = HlpBcm2709TimerDisarm;
  261. Timer.FunctionTable.AcknowledgeInterrupt =
  262. HlpBcm2709TimerAcknowledgeInterrupt;
  263. //
  264. // Register the BCM2709 ARM Timer based on the SP804. It is periodic and
  265. // readable, but can change dynamically in reduced power states. It also
  266. // supports "one-shot" mode in that the maximum next deadline can be
  267. // auto-programmed after a one-shot timer fires.
  268. //
  269. DefaultTimer = HlAllocateMemory(sizeof(BCM2709_TIMER),
  270. BCM2709_ALLOCATION_TAG,
  271. FALSE,
  272. NULL);
  273. if (DefaultTimer == NULL) {
  274. Status = STATUS_INSUFFICIENT_RESOURCES;
  275. goto Bcm2709TimerModuleEntryEnd;
  276. }
  277. DefaultTimer->Type = Bcm2709TimerArmPeriodic;
  278. DefaultTimer->Predivider = Predivider;
  279. Timer.Context = DefaultTimer;
  280. Timer.Features = TIMER_FEATURE_READABLE |
  281. TIMER_FEATURE_PERIODIC |
  282. TIMER_FEATURE_ONE_SHOT |
  283. TIMER_FEATURE_P_STATE_VARIANT;
  284. Timer.CounterBitWidth = 32;
  285. Timer.CounterFrequency = Frequency;
  286. Timer.Interrupt.Line.Type = InterruptLineGsi;
  287. Timer.Interrupt.Line.U.Gsi = HlBcm2709Table->ArmTimerGsi;
  288. Timer.Interrupt.TriggerMode = InterruptModeUnknown;
  289. Timer.Interrupt.ActiveLevel = InterruptActiveLevelUnknown;
  290. Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
  291. if (!KSUCCESS(Status)) {
  292. goto Bcm2709TimerModuleEntryEnd;
  293. }
  294. //
  295. // Register the BCM2709 ARM free running counter. It is readable but its
  296. // speed can change dynamically in reduced power status.
  297. //
  298. DefaultTimer = HlAllocateMemory(sizeof(BCM2709_TIMER),
  299. BCM2709_ALLOCATION_TAG,
  300. FALSE,
  301. NULL);
  302. if (DefaultTimer == NULL) {
  303. Status = STATUS_INSUFFICIENT_RESOURCES;
  304. goto Bcm2709TimerModuleEntryEnd;
  305. }
  306. DefaultTimer->Type = Bcm2709TimerArmCounter;
  307. DefaultTimer->Predivider = Predivider;
  308. Timer.Context = DefaultTimer;
  309. Timer.CounterBitWidth = 32;
  310. Timer.Features = TIMER_FEATURE_READABLE | TIMER_FEATURE_P_STATE_VARIANT;
  311. Timer.CounterFrequency = Frequency;
  312. Timer.Interrupt.Line.Type = InterruptLineInvalid;
  313. Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
  314. if (!KSUCCESS(Status)) {
  315. goto Bcm2709TimerModuleEntryEnd;
  316. }
  317. //
  318. // Initialize the BCM2709 System timer's free running counter. The counter
  319. // is writable, but since the Video Core maybe using it, altering the
  320. // counter is dangerous.
  321. //
  322. DefaultTimer = HlAllocateMemory(sizeof(BCM2709_TIMER),
  323. BCM2709_ALLOCATION_TAG,
  324. FALSE,
  325. NULL);
  326. if (DefaultTimer == NULL) {
  327. Status = STATUS_INSUFFICIENT_RESOURCES;
  328. goto Bcm2709TimerModuleEntryEnd;
  329. }
  330. RtlZeroMemory(DefaultTimer, sizeof(BCM2709_TIMER));
  331. DefaultTimer->Type = Bcm2709TimerSystemCounter;
  332. Timer.Context = DefaultTimer;
  333. Timer.CounterBitWidth = 64;
  334. Timer.Features = TIMER_FEATURE_READABLE;
  335. Timer.CounterFrequency = HlBcm2709Table->SystemTimerFrequency;
  336. Timer.Interrupt.Line.Type = InterruptLineInvalid;
  337. Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
  338. if (!KSUCCESS(Status)) {
  339. goto Bcm2709TimerModuleEntryEnd;
  340. }
  341. //
  342. // Initialize the two "periodic" BCM2709 System Timers that are not in use
  343. // by the GPU. Those are timers 1 and 3. They are not truly periodic in
  344. // that they do not automatically reload, but they make do for profiler
  345. // timers.
  346. //
  347. SystemTimer = HlAllocateMemory(sizeof(BCM2709_SYSTEM_TIMER),
  348. BCM2709_ALLOCATION_TAG,
  349. FALSE,
  350. NULL);
  351. if (SystemTimer == NULL) {
  352. Status = STATUS_INSUFFICIENT_RESOURCES;
  353. goto Bcm2709TimerModuleEntryEnd;
  354. }
  355. RtlZeroMemory(SystemTimer, sizeof(BCM2709_SYSTEM_TIMER));
  356. SystemTimer->Type = Bcm2709TimerSystemPeriodic1;
  357. Timer.Context = SystemTimer;
  358. Timer.CounterBitWidth = 32;
  359. Timer.Features = TIMER_FEATURE_READABLE |
  360. TIMER_FEATURE_PERIODIC |
  361. TIMER_FEATURE_ONE_SHOT;
  362. Timer.CounterFrequency = HlBcm2709Table->SystemTimerFrequency;
  363. Timer.Interrupt.Line.Type = InterruptLineGsi;
  364. Timer.Interrupt.Line.U.Gsi = HlBcm2709Table->SystemTimerGsiBase + 1;
  365. Timer.Interrupt.TriggerMode = InterruptModeUnknown;
  366. Timer.Interrupt.ActiveLevel = InterruptActiveLevelUnknown;
  367. Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
  368. if (!KSUCCESS(Status)) {
  369. goto Bcm2709TimerModuleEntryEnd;
  370. }
  371. SystemTimer = HlAllocateMemory(sizeof(BCM2709_SYSTEM_TIMER),
  372. BCM2709_ALLOCATION_TAG,
  373. FALSE,
  374. NULL);
  375. if (SystemTimer == NULL) {
  376. Status = STATUS_INSUFFICIENT_RESOURCES;
  377. goto Bcm2709TimerModuleEntryEnd;
  378. }
  379. RtlZeroMemory(SystemTimer, sizeof(BCM2709_SYSTEM_TIMER));
  380. SystemTimer->Type = Bcm2709TimerSystemPeriodic3;
  381. Timer.Context = SystemTimer;
  382. Timer.CounterBitWidth = 32;
  383. Timer.Features = TIMER_FEATURE_READABLE |
  384. TIMER_FEATURE_PERIODIC |
  385. TIMER_FEATURE_ONE_SHOT;
  386. Timer.CounterFrequency = HlBcm2709Table->SystemTimerFrequency;
  387. Timer.Interrupt.Line.Type = InterruptLineGsi;
  388. Timer.Interrupt.Line.U.Gsi = HlBcm2709Table->SystemTimerGsiBase + 3;
  389. Timer.Interrupt.TriggerMode = InterruptModeUnknown;
  390. Timer.Interrupt.ActiveLevel = InterruptActiveLevelUnknown;
  391. Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
  392. if (!KSUCCESS(Status)) {
  393. goto Bcm2709TimerModuleEntryEnd;
  394. }
  395. Bcm2709TimerModuleEntryEnd:
  396. return;
  397. }
  398. //
  399. // --------------------------------------------------------- Internal Functions
  400. //
  401. KSTATUS
  402. HlpBcm2709TimerInitialize (
  403. PVOID Context
  404. )
  405. /*++
  406. Routine Description:
  407. This routine initializes an SP804 timer.
  408. Arguments:
  409. Context - Supplies the pointer to the timer's context, provided by the
  410. hardware module upon initialization.
  411. Return Value:
  412. Status code.
  413. --*/
  414. {
  415. ULONG ControlValue;
  416. PHYSICAL_ADDRESS PhysicalAddress;
  417. ULONG Size;
  418. KSTATUS Status;
  419. PBCM2709_TIMER Timer;
  420. //
  421. // Map the hardware for the given timer if that has not been done.
  422. //
  423. Timer = (PBCM2709_TIMER)Context;
  424. if ((Timer->Type == Bcm2709TimerArmPeriodic) ||
  425. (Timer->Type == Bcm2709TimerArmCounter)) {
  426. if (HlBcm2709ArmTimerBase == NULL) {
  427. PhysicalAddress = HlBcm2709Table->ArmTimerPhysicalAddress;
  428. Size = Bcm2709ArmTimerRegisterSize;
  429. HlBcm2709ArmTimerBase = HlMapPhysicalAddress(PhysicalAddress,
  430. Size,
  431. TRUE);
  432. if (HlBcm2709ArmTimerBase == NULL) {
  433. Status = STATUS_INSUFFICIENT_RESOURCES;
  434. goto Bcm2709TimerInitializeEnd;
  435. }
  436. }
  437. } else {
  438. if (HlBcm2709SystemTimerBase == NULL) {
  439. PhysicalAddress = HlBcm2709Table->SystemTimerPhysicalAddress;
  440. Size = Bcm2709SystemTimerRegisterSize;
  441. HlBcm2709SystemTimerBase = HlMapPhysicalAddress(PhysicalAddress,
  442. Size,
  443. TRUE);
  444. if (HlBcm2709SystemTimerBase == NULL) {
  445. Status = STATUS_INSUFFICIENT_RESOURCES;
  446. goto Bcm2709TimerInitializeEnd;
  447. }
  448. }
  449. }
  450. //
  451. // Initialize the given timer.
  452. //
  453. switch (Timer->Type) {
  454. case Bcm2709TimerArmPeriodic:
  455. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerPredivider, Timer->Predivider);
  456. ControlValue = READ_ARM_TIMER_REGISTER(Bcm2709ArmTimerControl);
  457. ControlValue &= ~BCM2709_ARM_TIMER_CONTROL_INTERRUPT_ENABLE;
  458. ControlValue |= (BCM2709_ARM_TIMER_CONTROL_ENABLED |
  459. BCM2709_ARM_TIMER_CONTROL_DIVIDE_BY_1 |
  460. BCM2709_ARM_TIMER_CONTROL_32_BIT);
  461. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerLoadValue, 0xFFFFFFFF);
  462. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerControl, ControlValue);
  463. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerInterruptClear, 1);
  464. break;
  465. case Bcm2709TimerArmCounter:
  466. ControlValue = READ_ARM_TIMER_REGISTER(Bcm2709ArmTimerControl);
  467. ControlValue &= ~BCM2709_ARM_TIMER_CONTROL_FREE_RUNNING_DIVIDE_MASK;
  468. ControlValue |= (Timer->Predivider <<
  469. BCM2709_ARM_TIMER_CONTROL_FREE_RUNNING_DIVIDE_SHIFT) &
  470. BCM2709_ARM_TIMER_CONTROL_FREE_RUNNING_DIVIDE_MASK;
  471. ControlValue |= BCM2709_ARM_TIMER_CONTROL_FREE_RUNNING_ENABLED;
  472. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerControl, ControlValue);
  473. break;
  474. case Bcm2709TimerSystemPeriodic1:
  475. WRITE_SYSTEM_TIMER_REGISTER(Bcm2709SystemTimerControl,
  476. BCM2709_SYSTEM_TIMER_CONTROL_MATCH_1);
  477. break;
  478. case Bcm2709TimerSystemPeriodic3:
  479. WRITE_SYSTEM_TIMER_REGISTER(Bcm2709SystemTimerControl,
  480. BCM2709_SYSTEM_TIMER_CONTROL_MATCH_3);
  481. break;
  482. case Bcm2709TimerSystemCounter:
  483. break;
  484. default:
  485. Status = STATUS_INVALID_PARAMETER;
  486. goto Bcm2709TimerInitializeEnd;
  487. }
  488. Status = STATUS_SUCCESS;
  489. Bcm2709TimerInitializeEnd:
  490. return Status;
  491. }
  492. ULONGLONG
  493. HlpBcm2709TimerRead (
  494. PVOID Context
  495. )
  496. /*++
  497. Routine Description:
  498. This routine returns the hardware counter's raw value.
  499. Arguments:
  500. Context - Supplies the pointer to the timer's context.
  501. Return Value:
  502. Returns the timer's current count.
  503. --*/
  504. {
  505. ULONG High1;
  506. ULONG High2;
  507. ULONG Low;
  508. PBCM2709_TIMER Timer;
  509. ULONGLONG Value;
  510. Timer = (PBCM2709_TIMER)Context;
  511. switch (Timer->Type) {
  512. case Bcm2709TimerArmPeriodic:
  513. Value = 0xFFFFFFFF;
  514. Value -= READ_ARM_TIMER_REGISTER(Bcm2709ArmTimerCurrentValue);
  515. break;
  516. case Bcm2709TimerArmCounter:
  517. Value = READ_ARM_TIMER_REGISTER(Bcm2709ArmTimerFreeRunningCounter);
  518. break;
  519. case Bcm2709TimerSystemPeriodic1:
  520. case Bcm2709TimerSystemPeriodic3:
  521. Value = READ_SYSTEM_TIMER_REGISTER(Bcm2709SystemTimerCounterLow);
  522. break;
  523. case Bcm2709TimerSystemCounter:
  524. //
  525. // Do a high-low-high read to make sure sure the words didn't tear.
  526. //
  527. do {
  528. High1 = READ_SYSTEM_TIMER_REGISTER(Bcm2709SystemTimerCounterHigh);
  529. Low = READ_SYSTEM_TIMER_REGISTER(Bcm2709SystemTimerCounterLow);
  530. High2 = READ_SYSTEM_TIMER_REGISTER(Bcm2709SystemTimerCounterHigh);
  531. } while (High1 != High2);
  532. Value = (((ULONGLONG)High1) << 32) | Low;
  533. break;
  534. default:
  535. Value = 0;
  536. break;
  537. }
  538. return Value;
  539. }
  540. KSTATUS
  541. HlpBcm2709TimerArm (
  542. PVOID Context,
  543. TIMER_MODE Mode,
  544. ULONGLONG TickCount
  545. )
  546. /*++
  547. Routine Description:
  548. This routine arms the timer to fire an interrupt after the specified number
  549. of ticks.
  550. Arguments:
  551. Context - Supplies the pointer to the timer's context, provided by the
  552. hardware module upon initialization.
  553. Mode - Supplies the mode to arm the timer in. The system will never request
  554. a mode not supported by the timer's feature bits. The mode dictates
  555. how the tick count argument is interpreted.
  556. TickCount - Supplies the number of timer ticks from now for the timer to
  557. fire in. In absolute mode, this supplies the time in timer ticks at
  558. which to fire an interrupt.
  559. Return Value:
  560. STATUS_SUCCESS always.
  561. --*/
  562. {
  563. BCM2709_SYSTEM_TIMER_REGISTER CompareRegister;
  564. ULONG ControlValue;
  565. ULONG Counter;
  566. PBCM2709_SYSTEM_TIMER Timer;
  567. Timer = (PBCM2709_SYSTEM_TIMER)Context;
  568. if ((Timer->Type == Bcm2709TimerArmCounter) ||
  569. (Timer->Type == Bcm2709TimerSystemCounter)) {
  570. return STATUS_INVALID_PARAMETER;
  571. }
  572. if (TickCount > MAX_ULONG) {
  573. TickCount = MAX_ULONG;
  574. }
  575. switch (Timer->Type) {
  576. //
  577. // The ARM timer is armed by enabling it and setting the given ticks in the
  578. // load register. The timer will then count down, reloading said value once
  579. // the timer hits 0.
  580. //
  581. case Bcm2709TimerArmPeriodic:
  582. //
  583. // This Broadcom version of the SP804 does not appear to follow the
  584. // SP804 spec. with regards to the background load register. According
  585. // to the spec., when written, the load register is moved to the
  586. // current value register on the next rising edge of the clock. And
  587. // when the background value register is written, the value is stored
  588. // in the load register but not transferred to the current value
  589. // register until the current value reaches 0. Now, the spec. details
  590. // that if both the load register and background load register are
  591. // written before the next rising clock edge, that the current value
  592. // will be replaced by the value written to the load register (not the
  593. // background register) on the next clock edge. Unfortunately, the
  594. // Broadcom chip appears to load the background value into current
  595. // count if they are both written between clock edges.
  596. //
  597. // The workaround is to disable the counter, write the load register,
  598. // re-enable the counter to move the loaded value to the current value
  599. // register, and then, if necessary, write the background load value
  600. // register.
  601. //
  602. // Do not clear the interrupt here. On a multi-core system, this arm
  603. // request can race with the interrupt firing. If the interrupt were
  604. // cleared, then the other core would get interrupted only to find no
  605. // pending interrupts.
  606. //
  607. ControlValue = READ_ARM_TIMER_REGISTER(Bcm2709ArmTimerControl);
  608. ControlValue &= ~BCM2709_ARM_TIMER_CONTROL_ENABLED;
  609. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerControl, ControlValue);
  610. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerLoadValue, TickCount);
  611. ControlValue |= (BCM2709_ARM_TIMER_CONTROL_ENABLED |
  612. BCM2709_ARM_TIMER_CONTROL_DIVIDE_BY_1 |
  613. BCM2709_ARM_TIMER_CONTROL_32_BIT |
  614. BCM2709_ARM_TIMER_CONTROL_INTERRUPT_ENABLE);
  615. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerControl, ControlValue);
  616. if (Mode == TimerModeOneShot) {
  617. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerBackgroundLoadValue,
  618. 0xFFFFFFFF);
  619. }
  620. break;
  621. //
  622. // The System timers are armed by reading the low 32-bits of the counter,
  623. // adding the given ticks and setting that in the compare register. The
  624. // timer's interrupt will go off when the low 32-bits of the counter equals
  625. // the compare value.
  626. //
  627. case Bcm2709TimerSystemPeriodic1:
  628. case Bcm2709TimerSystemPeriodic3:
  629. CompareRegister = Bcm2709SystemTimerCompare1;
  630. ControlValue = BCM2709_SYSTEM_TIMER_CONTROL_MATCH_1;
  631. if (Timer->Type == Bcm2709TimerSystemPeriodic3) {
  632. CompareRegister = Bcm2709SystemTimerCompare3;
  633. ControlValue = BCM2709_SYSTEM_TIMER_CONTROL_MATCH_3;
  634. }
  635. Timer->Generation += 1;
  636. Timer->Mode = Mode;
  637. Timer->TickCount = (ULONG)TickCount;
  638. Timer->Generation += 1;
  639. WRITE_SYSTEM_TIMER_REGISTER(Bcm2709SystemTimerControl, ControlValue);
  640. Counter = READ_SYSTEM_TIMER_REGISTER(Bcm2709SystemTimerCounterLow);
  641. Counter += (ULONG)TickCount;
  642. WRITE_SYSTEM_TIMER_REGISTER(CompareRegister, Counter);
  643. break;
  644. default:
  645. return STATUS_INVALID_PARAMETER;
  646. }
  647. return STATUS_SUCCESS;
  648. }
  649. VOID
  650. HlpBcm2709TimerDisarm (
  651. PVOID Context
  652. )
  653. /*++
  654. Routine Description:
  655. This routine disarms the timer, stopping interrupts from firing.
  656. Arguments:
  657. Context - Supplies the pointer to the timer's context, provided by the
  658. hardware module upon initialization.
  659. Return Value:
  660. None.
  661. --*/
  662. {
  663. ULONG ControlValue;
  664. PBCM2709_SYSTEM_TIMER Timer;
  665. Timer = (PBCM2709_SYSTEM_TIMER)Context;
  666. switch (Timer->Type) {
  667. //
  668. // Disarm the ARM Timer by disabling its interrupt in the control register.
  669. //
  670. case Bcm2709TimerArmPeriodic:
  671. ControlValue = READ_ARM_TIMER_REGISTER(Bcm2709ArmTimerControl);
  672. ControlValue &= ~BCM2709_ARM_TIMER_CONTROL_INTERRUPT_ENABLE;
  673. ControlValue |= (BCM2709_ARM_TIMER_CONTROL_ENABLED |
  674. BCM2709_ARM_TIMER_CONTROL_DIVIDE_BY_1 |
  675. BCM2709_ARM_TIMER_CONTROL_32_BIT);
  676. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerControl, ControlValue);
  677. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerInterruptClear, 1);
  678. break;
  679. //
  680. // The System's periodic timers do not have an interrupt disable control
  681. // bit. Just leave the compare register programmed as is, but make sure it
  682. // does not get rearmed after it fires. At a frequency of 1MHz, the timer
  683. // will still expire every 71 minutes. So be it.
  684. //
  685. case Bcm2709TimerSystemPeriodic1:
  686. case Bcm2709TimerSystemPeriodic3:
  687. Timer->Generation += 1;
  688. Timer->Mode = TimerModeInvalid;
  689. Timer->TickCount = 0;
  690. Timer->Generation += 1;
  691. break;
  692. default:
  693. break;
  694. }
  695. return;
  696. }
  697. VOID
  698. HlpBcm2709TimerAcknowledgeInterrupt (
  699. PVOID Context
  700. )
  701. /*++
  702. Routine Description:
  703. This routine performs any actions necessary upon reciept of a timer's
  704. interrupt. This may involve writing to an acknowledge register to re-enable
  705. the timer to fire again, or other hardware specific actions.
  706. Arguments:
  707. Context - Supplies the pointer to the timer's context, provided by the
  708. hardware module upon initialization.
  709. Return Value:
  710. None.
  711. --*/
  712. {
  713. ULONG Compare;
  714. BCM2709_SYSTEM_TIMER_REGISTER CompareRegister;
  715. ULONG ControlValue;
  716. ULONG Counter;
  717. ULONG Generation1;
  718. ULONG Generation2;
  719. TIMER_MODE Mode;
  720. ULONG TickCount;
  721. PBCM2709_SYSTEM_TIMER Timer;
  722. Timer = (PBCM2709_SYSTEM_TIMER)Context;
  723. switch (Timer->Type) {
  724. //
  725. // Just write a 1 to the interrupt clear register to acknowledge the ARM
  726. // Timer's interrupt.
  727. //
  728. case Bcm2709TimerArmPeriodic:
  729. WRITE_ARM_TIMER_REGISTER(Bcm2709ArmTimerInterruptClear, 1);
  730. break;
  731. //
  732. // Acknowledge the interrupt by clearing the match bit in the control
  733. // register. If necessary, reprogram the compare register, as it does not
  734. // automatically get set for the next period. That said, if the compare
  735. // value has slipped behind the counter (possibly due to debugger
  736. // activity), make sure to schedule the next period in the future.
  737. //
  738. case Bcm2709TimerSystemPeriodic1:
  739. case Bcm2709TimerSystemPeriodic3:
  740. CompareRegister = Bcm2709SystemTimerCompare1;
  741. ControlValue = BCM2709_SYSTEM_TIMER_CONTROL_MATCH_1;
  742. if (Timer->Type == Bcm2709TimerSystemPeriodic3) {
  743. CompareRegister = Bcm2709SystemTimerCompare3;
  744. ControlValue = BCM2709_SYSTEM_TIMER_CONTROL_MATCH_3;
  745. }
  746. WRITE_SYSTEM_TIMER_REGISTER(Bcm2709SystemTimerControl, ControlValue);
  747. //
  748. // Loop attempting to get a consistent view of the mode and tick count.
  749. // If the read generations are not the same or both odd, it means that
  750. // the timer is actively being armed. Do not rearm it here. If the
  751. // generations read the same, a consistent view is found; arm the
  752. // timer. Unfortunately, this consistent view may be out of date by
  753. // the time it is armed. So, read the generation once again. If it got
  754. // updated, try to get another consistent view.
  755. //
  756. do {
  757. Generation1 = Timer->Generation;
  758. Mode = Timer->Mode;
  759. TickCount = Timer->TickCount;
  760. Generation2 = Timer->Generation;
  761. if ((Generation1 != Generation2) || ((Generation1 % 2) != 0)) {
  762. break;
  763. }
  764. if (Mode == TimerModeInvalid) {
  765. break;
  766. }
  767. Counter = READ_SYSTEM_TIMER_REGISTER(Bcm2709SystemTimerCounterLow);
  768. if (Mode == TimerModePeriodic) {
  769. Compare = READ_SYSTEM_TIMER_REGISTER(CompareRegister);
  770. Compare += TickCount;
  771. if (BCM2709_COUNTER_LESS_THAN(Counter, Compare) == FALSE) {
  772. Compare = Counter + TickCount;
  773. }
  774. } else {
  775. Compare = Counter + TickCount;
  776. }
  777. WRITE_SYSTEM_TIMER_REGISTER(CompareRegister, Compare);
  778. //
  779. // Read the generation again. If it is different, it means another
  780. // core ran through and either disabled or armed the timer. The
  781. // above arming may have been incorrect.
  782. //
  783. Generation1 = Timer->Generation;
  784. } while (Generation1 != Generation2);
  785. break;
  786. default:
  787. break;
  788. }
  789. return;
  790. }