am335tmr.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. am335tmr.c
  5. Abstract:
  6. This module implements support for the TI AM335x SoC DM timers.
  7. Author:
  8. Evan Green 6-Jan-2015
  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 <minoca/soc/am335x.h>
  21. #include "am335.h"
  22. //
  23. // --------------------------------------------------------------------- Macros
  24. //
  25. //
  26. // This macro reads from an AM335 timer. _Base should be a pointer, and
  27. // _Register should be a AM335_TIMER_REGISTER value.
  28. //
  29. #define READ_TIMER_REGISTER(_Base, _Register) \
  30. HlReadRegister32((_Base) + (_Register))
  31. //
  32. // This macro writes to an AM335 timer. _Base should be a pointer,
  33. // _Register should be AM335_TIMER_REGISTER value, and _Value should be a
  34. // ULONG.
  35. //
  36. #define WRITE_TIMER_REGISTER(_Base, _Register, _Value) \
  37. HlWriteRegister32((_Base) + (_Register), (_Value))
  38. //
  39. // ---------------------------------------------------------------- Definitions
  40. //
  41. //
  42. // ------------------------------------------------------ Data Type Definitions
  43. //
  44. /*++
  45. Structure Description:
  46. This structure stores the internal state associated with an AM335 DM
  47. timer.
  48. Members:
  49. Base - Stores the virtual address of the timer.
  50. PhysicalAddress - Stores the physical address of the timer.
  51. Index - Stores the zero-based index of this timer number.
  52. --*/
  53. typedef struct _AM335_TIMER_DATA {
  54. PVOID Base;
  55. PHYSICAL_ADDRESS PhysicalAddress;
  56. ULONG Index;
  57. } AM335_TIMER_DATA, *PAM335_TIMER_DATA;
  58. //
  59. // ----------------------------------------------- Internal Function Prototypes
  60. //
  61. KSTATUS
  62. HlpAm335TimerInitialize (
  63. PVOID Context
  64. );
  65. ULONGLONG
  66. HlpAm335TimerRead (
  67. PVOID Context
  68. );
  69. VOID
  70. HlpAm335TimerWrite (
  71. PVOID Context,
  72. ULONGLONG NewCount
  73. );
  74. KSTATUS
  75. HlpAm335TimerArm (
  76. PVOID Context,
  77. TIMER_MODE Mode,
  78. ULONGLONG TickCount
  79. );
  80. VOID
  81. HlpAm335TimerDisarm (
  82. PVOID Context
  83. );
  84. VOID
  85. HlpAm335TimerAcknowledgeInterrupt (
  86. PVOID Context
  87. );
  88. //
  89. // -------------------------------------------------------------------- Globals
  90. //
  91. PAM335X_TABLE HlAm335Table = NULL;
  92. //
  93. // ------------------------------------------------------------------ Functions
  94. //
  95. VOID
  96. HlpAm335TimerModuleEntry (
  97. VOID
  98. )
  99. /*++
  100. Routine Description:
  101. This routine is the entry point for the AM335 DM Timer hardware module.
  102. Its role is to detect and report the prescense of AM335 Timers.
  103. Arguments:
  104. None.
  105. Return Value:
  106. None.
  107. --*/
  108. {
  109. KSTATUS Status;
  110. TIMER_DESCRIPTION Timer;
  111. PAM335_TIMER_DATA TimerData;
  112. ULONG TimerIndex;
  113. HlAm335Table = HlGetAcpiTable(AM335X_SIGNATURE, NULL);
  114. if (HlAm335Table == NULL) {
  115. goto Am335TimerModuleEntryEnd;
  116. }
  117. //
  118. // Fire up the timer's power.
  119. //
  120. Status = HlpAm335InitializePowerAndClocks();
  121. if (!KSUCCESS(Status)) {
  122. goto Am335TimerModuleEntryEnd;
  123. }
  124. //
  125. // Register each of the independent timers in the timer block.
  126. //
  127. for (TimerIndex = 0; TimerIndex < AM335X_TIMER_COUNT; TimerIndex += 1) {
  128. //
  129. // Skip the timer if it has no address.
  130. //
  131. if (HlAm335Table->TimerBase[TimerIndex] == 0) {
  132. continue;
  133. }
  134. //
  135. // Skip timer 1 for now, as it has funky register offsets and not that
  136. // many timers are needed. Skip timer 0 as it seems to interact with
  137. // power management.
  138. //
  139. if ((TimerIndex == 1) || (TimerIndex == 0)) {
  140. continue;
  141. }
  142. RtlZeroMemory(&Timer, sizeof(TIMER_DESCRIPTION));
  143. Timer.TableVersion = TIMER_DESCRIPTION_VERSION;
  144. Timer.FunctionTable.Initialize = HlpAm335TimerInitialize;
  145. Timer.FunctionTable.ReadCounter = HlpAm335TimerRead;
  146. Timer.FunctionTable.WriteCounter = HlpAm335TimerWrite;
  147. Timer.FunctionTable.Arm = HlpAm335TimerArm;
  148. Timer.FunctionTable.Disarm = HlpAm335TimerDisarm;
  149. Timer.FunctionTable.AcknowledgeInterrupt =
  150. HlpAm335TimerAcknowledgeInterrupt;
  151. TimerData = HlAllocateMemory(sizeof(AM335_TIMER_DATA),
  152. AM335_ALLOCATION_TAG,
  153. FALSE,
  154. NULL);
  155. if (TimerData == NULL) {
  156. Status = STATUS_INSUFFICIENT_RESOURCES;
  157. goto Am335TimerModuleEntryEnd;
  158. }
  159. RtlZeroMemory(TimerData, sizeof(AM335_TIMER_DATA));
  160. TimerData->PhysicalAddress = HlAm335Table->TimerBase[TimerIndex];
  161. TimerData->Index = TimerIndex;
  162. Timer.Context = TimerData;
  163. Timer.Features = TIMER_FEATURE_READABLE |
  164. TIMER_FEATURE_WRITABLE |
  165. TIMER_FEATURE_PERIODIC |
  166. TIMER_FEATURE_ONE_SHOT;
  167. Timer.CounterBitWidth = AM335_TIMER_BIT_WIDTH;
  168. //
  169. // The first two timers run at a fixed frequency, but the rest run at
  170. // the system clock rate.
  171. //
  172. if ((TimerIndex == 0) || (TimerIndex == 1)) {
  173. Timer.CounterFrequency = AM335_TIMER_FREQUENCY_32KHZ;
  174. } else {
  175. Timer.CounterFrequency = 0;
  176. }
  177. Timer.Interrupt.Line.Type = InterruptLineControllerSpecified;
  178. Timer.Interrupt.Line.U.Local.Controller = 0;
  179. Timer.Interrupt.Line.U.Local.Line = HlAm335Table->TimerGsi[TimerIndex];
  180. Timer.Interrupt.TriggerMode = InterruptModeLevel;
  181. Timer.Interrupt.ActiveLevel = InterruptActiveLevelUnknown;
  182. Timer.Identifier = TimerIndex;
  183. //
  184. // Register the timer with the system.
  185. //
  186. Status = HlRegisterHardware(HardwareModuleTimer, &Timer);
  187. if (!KSUCCESS(Status)) {
  188. goto Am335TimerModuleEntryEnd;
  189. }
  190. }
  191. Am335TimerModuleEntryEnd:
  192. return;
  193. }
  194. //
  195. // --------------------------------------------------------- Internal Functions
  196. //
  197. KSTATUS
  198. HlpAm335TimerInitialize (
  199. PVOID Context
  200. )
  201. /*++
  202. Routine Description:
  203. This routine initializes an AM335 timer.
  204. Arguments:
  205. Context - Supplies the pointer to the timer's context, provided by the
  206. hardware module upon initialization.
  207. Return Value:
  208. Status code.
  209. --*/
  210. {
  211. KSTATUS Status;
  212. PAM335_TIMER_DATA Timer;
  213. ULONG Value;
  214. Timer = (PAM335_TIMER_DATA)Context;
  215. //
  216. // Map the hardware if that has not been done.
  217. //
  218. if (Timer->Base == NULL) {
  219. Timer->Base = HlMapPhysicalAddress(Timer->PhysicalAddress,
  220. AM335_TIMER_CONTROLLER_SIZE,
  221. TRUE);
  222. if (Timer->Base == NULL) {
  223. Status = STATUS_INSUFFICIENT_RESOURCES;
  224. goto Am335TimerInitializeEnd;
  225. }
  226. }
  227. //
  228. // Program the timer in free running mode with no interrupt.
  229. //
  230. WRITE_TIMER_REGISTER(Timer->Base,
  231. Am335TimerOcpConfig,
  232. AM335_TIMER_IDLEMODE_SMART);
  233. //
  234. // Disable wakeup functionality.
  235. //
  236. WRITE_TIMER_REGISTER(Timer->Base,
  237. Am335TimerInterruptWakeEnable,
  238. 0);
  239. //
  240. // Set the synchronous interface configuration register to non-posted mode,
  241. // which means that writes don't return until they complete. Posted mode
  242. // is faster for writes but requires polling a bit for reads.
  243. //
  244. WRITE_TIMER_REGISTER(Timer->Base,
  245. Am335TimerSynchronousInterfaceControl,
  246. 0);
  247. //
  248. // Disable all interrupts for now. The alternate register interface uses a
  249. // set/clear style for the interrupt mask bits.
  250. //
  251. WRITE_TIMER_REGISTER(Timer->Base,
  252. Am335TimerInterruptEnableClear,
  253. AM335_TIMER_INTERRUPT_MASK);
  254. //
  255. // Set the load value to zero to create a free-running timer, and reset the
  256. // current counter now too.
  257. //
  258. WRITE_TIMER_REGISTER(Timer->Base, Am335TimerLoad, 0);
  259. WRITE_TIMER_REGISTER(Timer->Base, Am335TimerCount, 0);
  260. //
  261. // Set the mode register to auto-reload, and start the timer.
  262. //
  263. Value = AM335_TIMER_OVERFLOW_TRIGGER |
  264. AM335_TIMER_STARTED |
  265. AM335_TIMER_AUTORELOAD;
  266. WRITE_TIMER_REGISTER(Timer->Base, Am335TimerControl, Value);
  267. //
  268. // Reset all interrupt-pending bits.
  269. //
  270. //
  271. WRITE_TIMER_REGISTER(Timer->Base,
  272. Am335TimerInterruptStatus,
  273. AM335_TIMER_INTERRUPT_MASK);
  274. Status = STATUS_SUCCESS;
  275. Am335TimerInitializeEnd:
  276. return Status;
  277. }
  278. ULONGLONG
  279. HlpAm335TimerRead (
  280. PVOID Context
  281. )
  282. /*++
  283. Routine Description:
  284. This routine returns the hardware counter's raw value.
  285. Arguments:
  286. Context - Supplies the pointer to the timer's context.
  287. Return Value:
  288. Returns the timer's current count.
  289. --*/
  290. {
  291. PAM335_TIMER_DATA Timer;
  292. ULONG Value;
  293. Timer = (PAM335_TIMER_DATA)Context;
  294. Value = READ_TIMER_REGISTER(Timer->Base, Am335TimerCount);
  295. return Value;
  296. }
  297. VOID
  298. HlpAm335TimerWrite (
  299. PVOID Context,
  300. ULONGLONG NewCount
  301. )
  302. /*++
  303. Routine Description:
  304. This routine writes to the timer's hardware counter. This routine will
  305. only be called for timers that have the writable counter feature bit set.
  306. Arguments:
  307. Context - Supplies the pointer to the timer's context, provided by the
  308. hardware module upon initialization.
  309. NewCount - Supplies the value to write into the counter. It is expected that
  310. the counter will not stop after the write.
  311. Return Value:
  312. None.
  313. --*/
  314. {
  315. PAM335_TIMER_DATA Timer;
  316. Timer = (PAM335_TIMER_DATA)Context;
  317. WRITE_TIMER_REGISTER(Timer->Base, Am335TimerCount, (ULONG)NewCount);
  318. return;
  319. }
  320. KSTATUS
  321. HlpAm335TimerArm (
  322. PVOID Context,
  323. TIMER_MODE Mode,
  324. ULONGLONG TickCount
  325. )
  326. /*++
  327. Routine Description:
  328. This routine arms the timer to fire an interrupt after the specified number
  329. of ticks.
  330. Arguments:
  331. Context - Supplies the pointer to the timer's context, provided by the
  332. hardware module upon initialization.
  333. Mode - Supplies the mode to arm the timer in. The system will never request
  334. a mode not supported by the timer's feature bits. The mode dictates
  335. how the tick count argument is interpreted.
  336. TickCount - Supplies the number of timer ticks from now for the timer to
  337. fire in. In absolute mode, this supplies the time in timer ticks at
  338. which to fire an interrupt.
  339. Return Value:
  340. STATUS_SUCCESS always.
  341. --*/
  342. {
  343. PAM335_TIMER_DATA Timer;
  344. ULONG Value;
  345. Timer = (PAM335_TIMER_DATA)Context;
  346. if (TickCount >= MAX_ULONG) {
  347. TickCount = MAX_ULONG - 1;
  348. }
  349. if (TickCount < 2) {
  350. TickCount = 2;
  351. }
  352. //
  353. // Start the timer ticking.
  354. //
  355. WRITE_TIMER_REGISTER(Timer->Base, Am335TimerControl, 0);
  356. WRITE_TIMER_REGISTER(Timer->Base, Am335TimerLoad, 0 - (ULONG)TickCount);
  357. WRITE_TIMER_REGISTER(Timer->Base, Am335TimerCount, 0 - (ULONG)TickCount);
  358. Value = AM335_TIMER_STARTED;
  359. if (Mode == TimerModePeriodic) {
  360. Value |= AM335_TIMER_AUTORELOAD;
  361. }
  362. WRITE_TIMER_REGISTER(Timer->Base,
  363. Am335TimerInterruptEnableSet,
  364. AM335_TIMER_OVERFLOW_INTERRUPT);
  365. WRITE_TIMER_REGISTER(Timer->Base, Am335TimerControl, Value);
  366. return STATUS_SUCCESS;
  367. }
  368. VOID
  369. HlpAm335TimerDisarm (
  370. PVOID Context
  371. )
  372. /*++
  373. Routine Description:
  374. This routine disarms the timer, stopping interrupts from firing.
  375. Arguments:
  376. Context - Supplies the pointer to the timer's context, provided by the
  377. hardware module upon initialization.
  378. Return Value:
  379. None.
  380. --*/
  381. {
  382. PAM335_TIMER_DATA Timer;
  383. //
  384. // Disable all interrupts.
  385. //
  386. Timer = (PAM335_TIMER_DATA)Context;
  387. WRITE_TIMER_REGISTER(Timer->Base,
  388. Am335TimerInterruptEnableClear,
  389. AM335_TIMER_INTERRUPT_MASK);
  390. //
  391. // Reset all pending interrupt bits.
  392. //
  393. WRITE_TIMER_REGISTER(Timer->Base,
  394. Am335TimerInterruptStatus,
  395. AM335_TIMER_INTERRUPT_MASK);
  396. return;
  397. }
  398. VOID
  399. HlpAm335TimerAcknowledgeInterrupt (
  400. PVOID Context
  401. )
  402. /*++
  403. Routine Description:
  404. This routine performs any actions necessary upon reciept of a timer's
  405. interrupt. This may involve writing to an acknowledge register to re-enable
  406. the timer to fire again, or other hardware specific actions.
  407. Arguments:
  408. Context - Supplies the pointer to the timer's context, provided by the
  409. hardware module upon initialization.
  410. Return Value:
  411. None.
  412. --*/
  413. {
  414. PAM335_TIMER_DATA Timer;
  415. Timer = (PAM335_TIMER_DATA)Context;
  416. //
  417. // Clear the overflow interrupt by writing a 1 to the status bit.
  418. //
  419. WRITE_TIMER_REGISTER(Timer->Base,
  420. Am335TimerInterruptStatus,
  421. AM335_TIMER_OVERFLOW_INTERRUPT);
  422. return;
  423. }