1
0

pwm.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*++
  2. Copyright (c) 2014 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. pwm.c
  9. Abstract:
  10. This module implements platform PWM support for the BCM2709 SoC family.
  11. Author:
  12. Chris Stevens 10-May-2017
  13. Environment:
  14. Firmware
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/lib/types.h>
  20. #include <minoca/fw/acpitabs.h>
  21. #include <minoca/soc/b2709os.h>
  22. #include <uefifw.h>
  23. #include <dev/bcm2709.h>
  24. //
  25. // --------------------------------------------------------------------- Macros
  26. //
  27. #define BCM2709_CLOCK_READ(_Register) \
  28. EfiReadRegister32(BCM2709_CLOCK_BASE + (_Register))
  29. #define BCM2709_CLOCK_WRITE(_Register, _Value) \
  30. EfiWriteRegister32(BCM2709_CLOCK_BASE + (_Register), _Value)
  31. //
  32. // ---------------------------------------------------------------- Definitions
  33. //
  34. //
  35. // Define the PWM clock's integer and fractional divisors. The frequency can be
  36. // calculated as:
  37. //
  38. // (source_frequency)
  39. // -----------------------------------------------
  40. // (integer_divisor + (fractional_divisor / 1024))
  41. //
  42. #define EFI_BCM2709_PWM_CLOCK_INTEGER_DIVISOR 5
  43. #define EFI_BCM2709_PWM_CLOCK_FRACTION_DIVISOR 0
  44. //
  45. // ------------------------------------------------------ Data Type Definitions
  46. //
  47. //
  48. // ----------------------------------------------- Internal Function Prototypes
  49. //
  50. //
  51. // -------------------------------------------------------------------- Globals
  52. //
  53. //
  54. // ------------------------------------------------------------------ Functions
  55. //
  56. EFI_STATUS
  57. EfipBcm2709PwmInitialize (
  58. VOID
  59. )
  60. /*++
  61. Routine Description:
  62. This routine initializes the PWM controller making sure that it is exposed
  63. on GPIO pins 40 and 45. This allows audio to be generated using PWM and it
  64. will go out the headphone jack. This also initializes the PWM clock to
  65. run at a reasonable rate.
  66. Arguments:
  67. None.
  68. Return Value:
  69. EFI status code.
  70. --*/
  71. {
  72. UINT32 Control;
  73. UINT32 Divisor;
  74. UINT64 Frequency;
  75. EFI_STATUS Status;
  76. PBCM2709_TABLE Table;
  77. Status = EfipBcm2709GpioFunctionSelect(BCM2709_GPIO_HEADPHONE_JACK_LEFT,
  78. BCM2709_GPIO_FUNCTION_SELECT_ALT_0);
  79. if (EFI_ERROR(Status)) {
  80. goto PwmInitializeEnd;
  81. }
  82. Status = EfipBcm2709GpioFunctionSelect(BCM2709_GPIO_HEADPHONE_JACK_RIGHT,
  83. BCM2709_GPIO_FUNCTION_SELECT_ALT_0);
  84. if (EFI_ERROR(Status)) {
  85. goto PwmInitializeEnd;
  86. }
  87. //
  88. // Disable the clock in order to changes its source and divisor.
  89. //
  90. Control = BCM2709_CLOCK_READ(Bcm2709ClockPwmControl);
  91. Control &= ~BCM2709_CLOCK_CONTROL_ENABLE;
  92. Control |= BCM2709_CLOCK_PASSWORD;
  93. BCM2709_CLOCK_WRITE(Bcm2709ClockPwmControl, Control);
  94. do {
  95. Control = BCM2709_CLOCK_READ(Bcm2709ClockPwmControl);
  96. } while ((Control & BCM2709_CLOCK_CONTROL_BUSY) != 0);
  97. //
  98. // Set the divisors.
  99. //
  100. Divisor = (EFI_BCM2709_PWM_CLOCK_INTEGER_DIVISOR <<
  101. BCM2709_CLOCK_DIVISOR_INTEGER_SHIFT) &
  102. BCM2709_CLOCK_DIVISOR_INTEGER_MASK;
  103. Divisor |= (EFI_BCM2709_PWM_CLOCK_FRACTION_DIVISOR <<
  104. BCM2709_CLOCK_DIVISOR_FRACTION_SHIFT) &
  105. BCM2709_CLOCK_DIVISOR_FRACTION_MASK;
  106. Divisor |= BCM2709_CLOCK_PASSWORD;
  107. BCM2709_CLOCK_WRITE(Bcm2709ClockPwmDivisor, Divisor);
  108. //
  109. // Change the clock source to PLLD. This runs at a base rate of 500MHz. The
  110. // spec recommends against changing this at the same time as enabling the
  111. // clock.
  112. //
  113. Control &= ~BCM2709_CLOCK_CONTROL_SOURCE_MASK;
  114. Control |= (BCM2709_CLOCK_CONTROL_SOURCE_PLLD <<
  115. BCM2709_CLOCK_CONTROL_SOURCE_SHIFT) &
  116. BCM2709_CLOCK_CONTROL_SOURCE_MASK;
  117. Control |= BCM2709_CLOCK_PASSWORD;
  118. BCM2709_CLOCK_WRITE(Bcm2709ClockPwmControl, Control);
  119. Control |= BCM2709_CLOCK_PASSWORD | BCM2709_CLOCK_CONTROL_ENABLE;
  120. BCM2709_CLOCK_WRITE(Bcm2709ClockPwmControl, Control);
  121. //
  122. // The PLLD source's base rate of 500MHz is the same as the min/max rate
  123. // advertised by the PWM clock via the video core mailbox. That said,
  124. // enabling the PWM clock via the mailbox seemingly breaks PWM audio, even
  125. // when trying clock sources other than PLLD. As a result, rather than
  126. // dynamically getting the base clock rate, grab it from the ACPI table and
  127. // then modify it by the given divisor.
  128. //
  129. Table = EfiGetAcpiTable(BCM2709_SIGNATURE, NULL);
  130. if (Table == NULL) {
  131. Status = EFI_NOT_FOUND;
  132. goto PwmInitializeEnd;
  133. }
  134. Divisor = (EFI_BCM2709_PWM_CLOCK_INTEGER_DIVISOR *
  135. BCM2709_CLOCK_DIVISOR_FRACTION_DENOMINATOR) +
  136. EFI_BCM2709_PWM_CLOCK_FRACTION_DIVISOR;
  137. Frequency = (ULONGLONG)Table->PwmClockFrequency *
  138. BCM2709_CLOCK_DIVISOR_FRACTION_DENOMINATOR;
  139. Frequency /= Divisor;
  140. if (Frequency > MAX_ULONG) {
  141. Status = EFI_UNSUPPORTED;
  142. goto PwmInitializeEnd;
  143. }
  144. Table->PwmClockFrequency = (UINT32)Frequency;
  145. EfiAcpiChecksumTable(Table,
  146. Table->Header.Length,
  147. OFFSET_OF(DESCRIPTION_HEADER, Checksum));
  148. PwmInitializeEnd:
  149. return Status;
  150. }
  151. //
  152. // --------------------------------------------------------- Internal Functions
  153. //